/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.openastexviewer;

/*
 * If you are reading Renderer.java do not edit this file.
 *
 * It is generated from Renderer.j by feeding through the c preprocessor.
 * Make any changes you need to make to Renderer.j and then regenerate
 * Renderer.java
 */
/**
 * This is the main set of methods for rendering objects within
 * OpenAstexViewer.
 *
 * This class manages a z-buffered, true colour pixel buffer for the scene
 * that is being displayed. The following types of objects can be rendered.
 *
 * - Lines (optionally with Wu antialiasing)
 * - Spheres (bitmap and and analytical)
 * - Cylinders (analytical)
 * - Triangles (with pseudo phong shading, transparency and texture mapping)
 * - Text (bitmap and 3d hershey fonts)
 *
 * Additionally, there is support for full screen supersampled antialiasing,
 * offscreen rendering to arbitrary sizes and simple shadow testing for the
 * objects that can be rendered.
 */



//#define STATISTICS





import java.io.*;
import java.util.*;
import java.util.zip.*;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class Renderer implements GLSurfaceView.Renderer  {
    /** Should we draw the image logo. */
    private boolean drawImageLogo = false;
    /** Shadow mode for the renderer. */
    public int shadowMode = ShadowsOff;
    /** Defines for the shadowing states. */
    public static final int ShadowsOff = 0;
    public static final int ShadowsAccumulate = 1;
    public static final int ShadowsOn = 2;
    /** Different settings for the render passes. */
    public static final int PreRenderPass = 1;
    public static final int RenderPass = 2;
    public static final int PostRenderPass = 4;
    public static final int FinalRenderPass = 8;
    /** Width of the renderer. */
    public int pixelWidth = 0;
    /** Height of the renderer. */
    public int pixelHeight = 0;
    /** Number of pixels. */
    public int pixelCount = 0;
    /** The pixel buffer. */
    public int pbuffer[] = null;
    /** The z-buffer. */
    public int zbuffer[] = null;
    /** Number of fixed precision bits. */
    public static final int FixedBits = 12;
    /** Floating point multiplier. */
    private static final double FFixedBits = (1 << FixedBits);
    /** Floating point multiplier for z-buffer interpolation. */
    public static final double ZFixedBits = (1 << (FixedBits+8));
    /** The centre point of the view. */
    private Point3d center = new Point3d();
    /** The width of the view. */
    public double width = 1.0;
    /** The distance behind the center of the back clip plane. */
    public double back = -1.0;
    /** The integer back plane. */
    public int backClip = 0;
    /** The distance in front of the center of the front clip plane. */
    public double front = 1.0;
    /** The integer front plane. */
    public int frontClip = 0;
    /** The number of frames we have drawn. */
    private int frameCount = 0;
    /** Is debugging information enabled? */
    public boolean debug = false;
    /** The list of objects to render. */
    public DynamicArray objects = new DynamicArray();
    /** The background ambient light for the renderer. */
    private int ambient = Color32.black;
    private int ambientr = 0;
    private int ambientg = 0;
    private int ambientb = 0;
    /** The style of lighting model. */
    private int lightingModel = DefaultLightingModel;
    /** The default lighting model. */
    public static final int DefaultLightingModel = 0;
    /** The cartoon style. */
    public static final int CartoonLightingModel = 1;
    /** The normal cutoff for cartoon rendering. */
    private double cartoonNormalCutoff = 0.08;
    /** Have we calcualated our light map? */
    public transient boolean lightMapCalculated = false;
    /** Have we initialised the single color lookup table? */
    public boolean colorInitialised = false;
    /** The list of lights. */
    public DynamicArray lights = new DynamicArray(8);
    /** The list of textures. */
    public Hashtable textures = new Hashtable();
    /** The current texture. */
    public Texture texture = null;
    /** Is clipping enabled? */
    public boolean clipping = false;
    /** The current transparency. */
    private int transparency = 0xff;
    /** The current transparency. */
    private boolean transparent = false;
    /** Does this triangle need clipping? */
    public boolean clipTriangle = false;
    /** Do RGB Gouraud shading rather than Phong lookup. */
    public boolean rgb = false;
    /** Do phong shading. */
    public boolean phong = true;
    /** Do backface removal of triangles. */
    public boolean frontFaceOnly = true;
    /** Are we backlighting back facing triangles. */
    public boolean backlit = false;
    /** Do depth cueing of the final image. */
    public boolean depthcue = false;
    /** Do we do analytical spheres? */
    public boolean analyticalSpheres = false;
    /** Minimum z-coordinate after transformation. */
    public int zmin = 0 ;
    /** Maximum z-coordinate after transformation. */
    public int zmax = 0;
    /** The base color for simple color triangle rendering. */
    public int triangleColor = 0;
    /** Components of the triangle color for fast shading */
    public int triangleColorR = 0;
    public int triangleColorG = 0;
    public int triangleColorB = 0;
    /** Are we doing triangle debugging color style. */
    public boolean colorTriangle = false;
    /** The current scale. */
    public double scale = 1.0;
    /** The current zoom. */
    public double zoom = 1.0;
    /** The clip increment. */
    public double clipIncrement = 0.5;
    /** The background colour. */
    private int background = 0xff000000;
    /** Components of the background for fast depth cueing. */
    private int rbackground = 0;
    private int gbackground = 0;
    private int bbackground = 0;
    /** Gradient at top of screen. */
    private int gradientTop = 0x000000;
    /** Gradient at bottom of screen. */
    private int gradientBottom = 0x000000;
    /* Are we coloring background with a gradient. */
    private boolean backgroundGradient = false;
    /** String to display. */
    private String statusString = null;
    /** Logo to display. */
    private String logo = null;
    /** Contrasting colors for debugging renderer performance. */
    public int debugColor[] = {
 0xff9999, 0x99ff99, 0x9999ff, 0xffff99, 0xff99ff, 0x99ffff
    };
    /** Primary colors for specific triangle issues. */
    public int triangleColors[] = {
 Color32.white, 0xff9999, 0x99ff99, 0x9999ff,
 Color32.red, Color32.green, Color32.blue, Color32.brown
    };
    public transient DynamicArray rendererEventListeners = new DynamicArray();
    /** Are we antialiasing. */
    public boolean antialias = false;
    /** Are we doing wu line antialiasing. */
    public boolean wuAntiAlias = false;
    /** Did antialiasing mode change? */
    private boolean antialiasModeChanged = false;
    /** Return the current antialiasing setting. */
    public boolean getAntiAlias(){
 return antialias;
    }
    /** Set the current antialiasing setting. */
    public void setAntiAlias(boolean b){
 if(antialias != b){
     antialias = b;
     antialiasModeChanged = true;
     if(antialias){
  setSamples(2);
     }else{
  setSamples(1);
     }
 }
    }
    /** Set the lighting model. */
    public void setLightingModel(int lm){
 lightingModel = lm;
 calculateLightMap();
    }
    public int getLightingModel(){
 return lightingModel;
    }
    /** Set the cartoon normal cutoff. */
    public void setCartoonNormalCutoff(double c){
 cartoonNormalCutoff = c;
 calculateLightMap();
    }
    /** Get the cartoon normal cutoff. */
    public double getCartoonNormalCutoff(){
 return cartoonNormalCutoff;
    }
    /** Set the background colour. */
    public void setBackgroundColor(int c){
 background = c;
    }
    /** Get the background colour. */
    public int getBackgroundColor(){
 return background;
    }
    /** Set top of background colour gradient. */
    public void setGradient(boolean b){
        backgroundGradient = b;
    }
    /** Set top of background colour gradient. */
    public void setGradientTop(int c){
        gradientTop = c;
    }
    /** Set top of background colour gradient. */
    public void setGradientBottom(int c){
        gradientBottom = c;
    }
    /** Set the scale. */
    public void setScale(double s){
 scale = s;
    }
    /** Get the scale. */
    public double getScale(){
 return scale;
    }
    /** Set the zoom. */
    public void setZoom(double s){
 zoom = s;
    }
    /** Get the zoom. */
    public double getZoom(){
 return zoom;
    }
    /** The minimum zoom we allow. */
    private static final double minimumZoom = 0.001;
    /** Change the zoom for the molecule. */
    public void applyZoom(double factor){
        zoom *= (1. + factor);
        if(zoom < minimumZoom){
            zoom = minimumZoom;
        }
    }
    /** Set the radius. */
    public void setRadius(double d){
 //FILE.out.print("radius set to %.1f\n", d);
 if(d < 4.0){
     //Exception e = new Exception("setRadius");
     //e.printStackTrace();
     d = 4.0;
 }
 width = d;
    }
    /** Get the radius. */
    public double getRadius(){
 return width;
    }
    /** Set the front clip plane. */
    public void setFrontClip(double v){
 setFrontClip(v, true);
    }
    public double getFrontClip(){
 return front;
    }
    public void setFrontClip(double v, boolean fireEvent){
 front = v;
 if(front < clipIncrement){
     front = clipIncrement;
 }
 if(fireEvent){
     RendererEvent re =
  new RendererEvent(RendererEvent.FrontClipMoved,
      new Double(front));
     fireRendererEvent(re);
 }
    }
    /** Set the back clip plane. */
    public void setBackClip(double v){
 setBackClip(v, true);
    }
    public double getBackClip(){
 return back;
    }
    public void setBackClip(double v, boolean fireEvent){
 back = v;
 if(-back < clipIncrement){
     back = -clipIncrement;
 }
 if(fireEvent){
     RendererEvent re =
  new RendererEvent(RendererEvent.BackClipMoved,
      new Double(back));
     fireRendererEvent(re);
 }
    }
    /** Set the clip. */
    public void setClip(double f, double b){
 setFrontClip(f);
 setBackClip(b);
    }
    /** Set the clip. */
    public void setClip(double d){
 //System.out.println("setClip " + d);
 if(d < clipIncrement){
     d = clipIncrement;
 }
 setFrontClip(d);
 setBackClip(-d);
 //System.out.println("front " + front);
 //System.out.println("back " + back);;
    }
    /** Set the clip. */
    public double getClip(){
 return front;
    }
    /** Set the clip increment. */
    public void setClipIncrement(double inc){
        clipIncrement = inc;
    }
    /** Increment the clip plane distance. */
    public void incrementClip(){
 setFrontClip(front + clipIncrement);
 setBackClip(back - clipIncrement);
    }
    /** Decrement the clip plane distance. */
    public void decrementClip(){
 setFrontClip(front - clipIncrement);
 setBackClip(back + clipIncrement);
    }
    /** Increment the clip plane distance. */
    public void incrementFrontClip(){
 setFrontClip(front + clipIncrement);
    }
    /** Decrement the clip plane distance. */
    public void decrementFrontClip(){
 setFrontClip(front - clipIncrement);
    }
    /** Get the center point. */
    public Point3d getCenter(){
 return new Point3d(center);
    }
    /** Set the center point. */
    public void setCenter(Point3d p){
 center.x = p.x;
 center.y = p.y;
 center.z = p.z;
    }
    /** Reset the center and radius of the view. */
    public void resetCenterAndRadius(){
        center.x = center.y = center.z = 0.0;
        width = 0.0;
        //scaleInitialised = false;
        //centerInitialised = false;
    }
    /** Default renderer. */
    public Renderer(){
 for(int i = 0; i < 100; i++){
     String light = "light" + i;
     if(Settings.get("config", light + ".on") == null){
  break;
     }
     boolean onoff = Settings.getBoolean("config", light + ".on");
     double x = Settings.getDouble("config", light + ".x");
     double y = Settings.getDouble("config", light + ".y");
     double z = Settings.getDouble("config", light + ".z");
     int diffuser = Settings.getInteger("config", light + ".diffuse.r");
     int diffuseg = Settings.getInteger("config", light + ".diffuse.g");
     int diffuseb = Settings.getInteger("config", light + ".diffuse.b");
     int highlightr = Settings.getInteger("config", light + ".highlight.r");
     int highlightg = Settings.getInteger("config", light + ".highlight.g");
     int highlightb = Settings.getInteger("config", light + ".highlight.b");
     double power = Settings.getDouble("config", light + ".phongpower");
     int diffuse = Color32.pack(diffuser, diffuseg, diffuseb);
     int highlight = Color32.pack(highlightr, highlightg, highlightb);
     addLight(onoff, x, y, z, diffuse, highlight, power);
 }
 int ar = Settings.getInteger("config", "ambient.r", 32);
 int ag = Settings.getInteger("config", "ambient.g", 32);
 int ab = Settings.getInteger("config", "ambient.b", 32);
 hersheyScale = Settings.getDouble("fonts", "hershey.scale", 0.04);
 hersheyRadius = Settings.getDouble("fonts", "hershey.radius", 0.04);
 setAmbient(Color32.pack(ar, ag, ab));
 lineRadius = Settings.getDouble("config", "maplineradius", 0.02);
 drawImageLogo = Settings.getBoolean("config", "draw.logo", false);
    }
    /** Set the ambient component of the lighting model. */
    public void setAmbient(int rgb){
 ambient = rgb;
 ambientr = Color32.getRed(rgb);
 ambientg = Color32.getGreen(rgb);
 ambientb = Color32.getBlue(rgb);
 calculateLightMap();
    }
    /** Get the ambient component. */
    public int getAmbient(){
 return ambient;
    }
    /** Calculate index of x,y screen coordinate. */
    private final int INDEX(int x, int y) {
 return y * pixelWidth + x;
    }
    /** Add a light. */
    public void addLight(boolean onOff,
    double x, double y, double z,
    int diffuseColor, int specularColor,
    double power){
 Light l = new Light(onOff, x, y, z, diffuseColor, specularColor, power);
 lights.add(l);
    }
    /** Add a listener for renderer events. */
    public void addRendererEventListener(RendererEventListener rel){
        if(rendererEventListeners == null){
            rendererEventListeners = new DynamicArray();
        }
        rendererEventListeners.add(rel);
    }
    /** Remove a listener for renderer events. */
    public void removeRendererEventListener(RendererEventListener rel){
        if(rendererEventListeners != null){
            rendererEventListeners.remove(rel);
        }
    }
    /** Pass of the renderer event to anything that is interested. */
    private void fireRendererEvent(RendererEvent re){
        if(rendererEventListeners != null){
            int listenerCount = rendererEventListeners.size();
            for(int i = 0; i < listenerCount; i++){
                RendererEventListener rel =
                    (RendererEventListener)rendererEventListeners.get(i);
                rel.handleRendererEvent(re);
            }
        }
    }
    /** Add a tmesh to the renderer. */
    public void addTmesh(Tmesh tm){
 System.out.println("adding tmesh " + tm);
 String newName = tm.getName();
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = 0; i < graphicalObjectCount; i++){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && newName != null &&
        newName.equals(name)){
  System.out.println("remove graphical object: "+name);
  removeGraphicalObjects(object);
     }
 }
 objects.add(tm);
 RendererEvent re = new RendererEvent(RendererEvent.ObjectAdded, tm);
 tm.setRenderer(this);
 fireRendererEvent(re);
    }
    /** Add a graphical object. */
    public void addGraphicalObject(Tmesh tm){
 addTmesh(tm);
    }
    /** Get the number of graphical objects. */
    public int getGraphicalObjectCount(){
 return objects.size();
    }
    /** Get the number of graphical objects. */
    public Tmesh getGraphicalObject(int i){
 return (Tmesh)objects.get(i);
    }
    /** Return a GraphicalObject with the specified name. */
    public Tmesh getGraphicalObject(String targetName){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = graphicalObjectCount - 1; i >= 0; i--){
            Tmesh object = (Tmesh)objects.get(i);
            String name = object.getName();
     if(name != null && targetName != null &&
        name.equals(targetName)){
  return object;
     }
 }
 return null;
    }
    /** Return a DynamicArray of objects that match the pattern. */
    public DynamicArray getGraphicalObjects(String pattern){
 DynamicArray matchingObjects = new DynamicArray();
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = 0; i < graphicalObjectCount; i++){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
  matchingObjects.add(object);
     }
 }
 return matchingObjects;
    }
    /** Change the visibility state of GraphicalObjects. */
    public void setGraphicalObjectsColour(String pattern, int colour){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = graphicalObjectCount - 1; i >= 0; i--){
            Tmesh object = (Tmesh)objects.get(i);
            String name = object.getName();
           if(name != null && match.matches(pattern, name)){
                object.setColor(colour);
            }
        }
    }
    /** Change the visibility state of GraphicalObjects. */
    public void setGraphicalObjectsVisibility(String pattern, int state){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = graphicalObjectCount - 1; i >= 0; i--){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
                if(state == 0){
                    object.setVisible(false);
                }else if(state == 1){
                    object.setVisible(true);
                }else if(state == 2){
                    // state 2 is toggle
                    if(object.isVisible()){
                        object.setVisible(false);
                    }else{
                        object.setVisible(true);
                    }
                }
            }
        }
    }
    /** Remove objects that match a pattern. */
    public void removeGraphicalObjects(String pattern){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = graphicalObjectCount - 1; i >= 0; i--){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
  removeGraphicalObjects(object);
            }
        }
    }
    /** Remove specific graphical object. */
    public void removeGraphicalObjects(Tmesh tm){
 if(objects.contains(tm)){
     objects.remove(tm);
     tm.setRenderer(null);
     RendererEvent re =
  new RendererEvent(RendererEvent.ObjectRemoved, tm);
     fireRendererEvent(re);
 }
    }
    /** Remove objects that begin with the specified string. */
    public void removeGraphicalObjectsBeginningWith(String prefix){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = graphicalObjectCount - 1; i >= 0; i--){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && name.startsWith(prefix)){
  removeGraphicalObjects(object);
            }
        }
    }
    /** Apply texture to the specified objects. */
    public void applyTexture(String pattern, String texName){
 Texture tex = null;
 if(texName != null && texName.equals("off") == false){
     tex = (Texture)textures.get(texName);
     if(tex == null){
  System.out.println("applyTexture: couldn't find texture " +
       texName);
  return;
     }
 }
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = 0; i < graphicalObjectCount; i++){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
  object.texture = tex;
     }
 }
    }
    /** Scale texture to the specified objects. */
    public void scaleTexture(String pattern, int attribute, double value){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = 0; i < graphicalObjectCount; i++){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
  //System.out.println("setting " + attribute + " to " + value);
  if(attribute == Tmesh.UScale){
      object.setUScale(value);
  }else if(attribute == Tmesh.VScale){
      object.setVScale(value);
  }else if(attribute == Tmesh.UOffset){
      object.setUOffset(value);
  }else if(attribute == Tmesh.VOffset){
      object.setVOffset(value);
  }else{
      System.out.println("scaleTexture: unknown attribute " + attribute);
  }
     }
 }
    }
    /** Set backface on or off. */
    public void setBackface(String pattern, boolean value){
        int graphicalObjectCount = getGraphicalObjectCount();
        for(int i = 0; i < graphicalObjectCount; i++){
            Tmesh object = getGraphicalObject(i);
            String name = object.getName();
            if(name != null && match.matches(pattern, name)){
  object.setBackface(value);
     }
 }
    }
    /** Clear both buffers simultaneously. */
    private void clearBuffers(){
 int i;
 int zb[] = zbuffer;
 int pb[] = pbuffer;
 int pc = pixelCount;
 int zbmin = Integer.MIN_VALUE;
 backClip = (int)(back * ZFixedBits);
 frontClip = (int)(front * ZFixedBits);
 zbmin = backClip;
 rbackground = Color32.getRed(background);
 gbackground = Color32.getGreen(background);
 bbackground = Color32.getBlue(background);
        // slightly faster even though it goes backwards
        for(i = pc; --i >= 0;){
            zb[i] = zbmin;
        }
        if(backgroundGradient){
            int index = 0;
            int rt = Color32.getRed(gradientTop);
            int gt = Color32.getGreen(gradientTop);
            int bt = Color32.getBlue(gradientTop);
            int rb = Color32.getRed(gradientBottom);
            int gb = Color32.getGreen(gradientBottom);
            int bb = Color32.getBlue(gradientBottom);
            for(i = 0; i < pixelHeight; i++){
                int rowcol = 0;
                double frac = (double)i/(double)(pixelHeight - 1);
                int rrow = rt + (int)(frac * (double)(rb - rt));
                int grow = gt + (int)(frac * (double)(gb - gt));
                int brow = bt + (int)(frac * (double)(bb - bt));
                rowcol = (rrow<<16)|(grow<<8)|brow;
                // set all pixels on this row
                for(int j = 0; j < pixelWidth; j++){
                    pb[index++] = rowcol;
                }
            }
        }else{
            for(i = pc; --i >= 0;){
                pb[i] = background;
            }
        }
        if(!depthMapInitialised){
            initialiseDepthMap();
        }
 // get rid of transparent spheres from the cache
 stransx.removeAllElements();
 stransy.removeAllElements();
 stransz.removeAllElements();
 stransr.removeAllElements();
 stransxt.removeAllElements();
 stransyt.removeAllElements();
 stranszt.removeAllElements();
 stransrt.removeAllElements();
 stransrgb.removeAllElements();
 stransp.removeAllElements();
 stransid.removeAllElements();
    }
    /** Fancy clear buffers. */
    private void clearBuffers2(){
 int zb[] = zbuffer;
 int pb[] = pbuffer;
 int zbmin = Integer.MIN_VALUE;
 int entry = 0;
 int i, j, blue;
 backClip = (int)(back * ZFixedBits);
 frontClip = (int)(front * ZFixedBits);
 zbmin = backClip;
 for(j = 0; j < pixelHeight; j++){
     blue = (int)(255*(double)(pixelHeight-j)/(double)pixelHeight);
     if(blue > 255) blue = 255;
     if(blue < 0) blue = 0;
     int color = Color32.pack(0, 0, blue);
     for(i = 0; i < pixelWidth; i++){
  zb[entry] = zbmin;
  pb[entry] = color;
  entry++;
     }
 }
    }
    private boolean depthMapInitialised = false;
    private int depthScale[] = new int[257];
    private double fogDensity = 0.012;
    /** Initialise the depth map for depth mapping. */
    private void initialiseDepthMap(){
 for(int i = 0; i < 257; i++){
     // the depth ranging sometimes generates 256...
     // not sure why, just add it to the table.
     if(i == 256){
  depthScale[i] = 255;
     }else{
  depthScale[i] = 255 - (int)(255*(Math.pow(Math.E, fogDensity*(255-i)) - 1)/
         (Math.pow(Math.E, fogDensity*255)-1));
  //System.out.println("depthScale["+i+"] = " + depthScale[i]);
     }
 }
 depthMapInitialised = true;
    }
    public boolean emulate555 = false;
    /** Post process the buffers. */
    public void postProcess(){
 drawObjects(PostRenderPass);
 if(depthcue){
     //System.out.println("depthcue");
     int zb[] = zbuffer;
     int pb[] = pbuffer;
     zmax = frontClip;
     zmin = backClip;
     // if we were clipping we might have to
     // change the boundary of the depthcueing
     if(frontClip < zmax){
  zmax = frontClip;
     }
     if(backClip > zmin){
  zmin = backClip;
     }
     int range = zmax - zmin;
     int shade;
     range = range >> 8;
     // background is white, fog to white
     if(background == Color32.white){
  for(int i = 0; i < pixelCount; i++){
      //System.out.println("zb[i] " + zb[i]);
      if(zb[i] != zmin){
   int scale = (zb[i] - zmin)/range;
   //scale *= scale;
   //shade = (int)(170 + 85 * scale);
   //System.out.println("range " + range + " scale " + scale);
   shade = depthScale[scale];
   // can do this with & somehow
   int whiteComponent = Color32.scale(0xcccccc, 255 - shade);
   int pixelComponent = Color32.scale(pb[i], shade);
   pb[i] = Color32.add(whiteComponent, pixelComponent);
      }
  }
     }else{
  for(int i = 0; i < pixelCount; i++){
      //System.out.println("zb[i] " + zb[i]);
      if(zb[i] != zmin){
   int scale = (zb[i] - zmin)/range;
   //scale *= scale;
   //shade = (int)(170 + 85 * scale);
   //System.out.println("range " + range + " scale " + scale);
   shade = depthScale[scale];
   pb[i] = Color32.scale(pb[i], shade);
      }
  }
     }
 }
 // draw any of the strings we have loaded.
 drawLogo();
 drawStatusString();
 // apply 2x2 super sampled anti aliasing
 if(antialias){
     int pcount = 0;
     int index = 0;
     int pb[] = pbuffer;
     for(int j = 0; j < pixelHeight; j += 2){
  for(int i = 0; i < pixelWidth; i += 2){
      int r = 0, g = 0, b =0;
      int p0 = pb[index];
      int p1 = pb[index+1];
      int p2 = pb[index+pixelWidth];
      int p3 = pb[index+pixelWidth+1];
      if(p0 == background && p1 == background &&
         p2 == background && p3 == background){
   opbuffer[pcount++] = background;
      }else{
   r = (p0 & 0xff0000);
   g = (p0 & 0xff00);
   b = (p0 & 0xff);
   r += (p1 & 0xff0000);
   g += (p1 & 0xff00);
   b += (p1 & 0xff);
   r += (p2 & 0xff0000);
   g += (p2 & 0xff00);
   b += (p2 & 0xff);
   r += (p3 & 0xff0000);
   g += (p3 & 0xff00);
   b += (p3 & 0xff);
   r >>= 2;
   g >>= 2;
   b >>= 2;
   opbuffer[pcount++] =
       ((r)&0xff0000)|((g)&0xff00)|(b);
      }
      index += 2;
  }
  // need to skip row
  index += pixelWidth;
  //index += 3*w;
     }
 }
 drawObjects(FinalRenderPass);
 //if(drawImageLogo){
 drawImage();
 //}
    }
    /** Check sum validity stuff. */
    private boolean imageValidityChecked = false;
    private boolean imageValid = false;
    /** Checksum bytes (for obfuscation). */
    //private static final int csByte0 =  38;
    //private static final int csByte1 =  85;
    //private static final int csByte2 =   2;
    //private static final int csByte3 = 245;
    //private static final int csByte0 =  49;
    //private static final int csByte1 =  54;
    //private static final int csByte2 =  42;
    //private static final int csByte3 =  16;
    private static final int csByte0 = 110;
    private static final int csByte1 = 82;
    private static final int csByte2 = 154;
    private static final int csByte3 = 240;
    /** Draw the image. */
    private final void drawImage(){
 int width = AstexLogo.width;
 int height = AstexLogo.height;
 if(imageValidityChecked == false){
     int checkSum = CRC32.crc32(AstexLogo.pixels, 0, width*height, -1);
     if(((csByte0)|(csByte1<<8)|(csByte2<<16)|(csByte3<<24)) != checkSum){
  imageValid = false;
     }else{
  imageValid = true;
     }
     imageValidityChecked = true;
 }
 //FILE.out.print("checkSum %d\n", checkSum);
 if(imageValid){
            // on screen antialiasing behaves differently to 
            // off screen antialiasing.
     int hs = antialias ? pixelHeight/getSamples() : pixelHeight;
     int ws = antialias ? pixelWidth/getSamples() : pixelWidth;
     for(int y = 0; y < height; y++){
  int yp = hs - height + y - 1 ;
  for(int x = 0; x < width; x++){
      int xp = ws - width + x;
      int imageIndex = x + y * width;
      int p = AstexLogo.pixels[imageIndex];
      int rgb = (p & 0xffffff);
      if(rgb != 0){
   int interp = ((p >> 24) & 0xff);
                        if(interp != 0){
                            if(antialias){
                                int bg = opbuffer[xp + yp*ws];
                                int newp = Color32.blend(rgb, bg, interp);
                                opbuffer[xp + yp*ws] = newp;
                            }else{
                                int bg = pbuffer[xp + yp*ws];
                                int newp = Color32.blend(rgb, bg, interp);
                                setPixel(xp, yp, newp);
                            }
                        }
   //setPixel(xp, yp, newp);
      }
  }
     }
 }else{
     // if someone fiddled with the image they get a grayscale image for
     // their trouble
     int pixel = 0;
     for(int y = 0; y < pixelHeight; y++){
  for(int x = 0; x < pixelWidth; x++){
      pbuffer[pixel] = Color32.getGrayScale(pbuffer[pixel]);
      pixel++;
  }
     }
 }
    }
    /** Minimum intensity for depth cueing. */
    private int DepthCueMin = 50;
    /** Apply depth cueing to a color. */
    private int depthCueColor(int c, int z){
 int intensity = 0;
 if(z > frontClip){
     intensity = 255;
 }else if(z < backClip){
     intensity = DepthCueMin;
 }else{
     double scale = (double)(z - backClip)/
  (double)(frontClip - backClip);
     intensity = (int)(DepthCueMin + (255-DepthCueMin)*scale);
 }
 if(background == Color32.black){
     return Color32.scale(c, intensity);
 }else{
     return Color32.blend(c, background, intensity);
 }
    }
    /** Apply depth cueing to a color for a shaded object. */
    private int depthCueShadedColor(int c, int z){
 if(!depthMapInitialised){
     initialiseDepthMap();
 }
 //FILE.out.print("z %10d", z);
 //FILE.out.print(" frontClip %10d", frontClip);
 //FILE.out.print(" backClip  %10d ", backClip);
 int zslot = (int)(255.0*(z-backClip)/(double)(frontClip-backClip));
 //FILE.out.print(" zslot  %10d\n", zslot);
 if(zslot < 0){
     zslot = 0;
 }else if(zslot > 255){
     zslot = 255;
 }
 int ds = depthScale[zslot];
 if(background == Color32.black){
     return Color32.scale(c, ds);
 }else{
     return Color32.blend(c, background, ds);
 }
    }
    /** The transparent sphere we are drawing. */
    private int transparentSphere = -1;
    /** Draw the tmesh objects. */
    public void drawObjects(){
 // first pass, draw opaque objects
 for(int i = 0; i < objects.size(); i++){
     Tmesh tm = (Tmesh)objects.get(i);
     if(tm.getRenderPass() == RenderPass){
  if(tm.isVisible() && tm.transparency == 255){
      renderObject(tm);
  }
     }
 }
 // third pass, draw transparent objects
 for(int i = 0; i < objects.size(); i++){
     Tmesh tm = (Tmesh)objects.get(i);
     if(tm.getRenderPass() == RenderPass){
  if(tm.isVisible() && tm.transparency != 255){
      renderObject(tm);
  }
     }
 }
        renderTransparentSpheres();
    }
    /** Draw object that belong to a particular render pass. */
    private void drawObjects(int pass){
 for(int i = 0; i < objects.size(); i++){
     Tmesh tm = (Tmesh)objects.get(i);
     if(tm.getRenderPass() == pass){
  if(tm.isVisible()){
      renderObject(tm);
  }
     }
 }
    }
    /** List of spheres that overlap current transparent sphere. */
    private IntArray sphereOverlaps = new IntArray();
    /** Render the spheres that are in the transparent list. */
    private void renderTransparentSpheres(){
 int sphereCount = stransx.size();
 double stx[] = stransx.getArray();
 double sty[] = stransy.getArray();
 double stz[] = stransz.getArray();
 double str[] = stransr.getArray();
 // second pass, draw transparent spheres
 if(sphereCount > 0){
     double sz[] = stranszt.getArray();
     int ids[] = stransid.getArray();
     indexSort(sz, ids, 0, sphereCount - 1);
     for(int pass = 0; pass < 2; pass++){
  //for(int id = 0; id < sphereCount; id++){
  for(int id = sphereCount - 1; id >= 0; id--){
      int i = ids[id];
      if(pass == 0 && stransp.get(i) > 200 ||
         pass == 1 && stransp.get(i) <= 200){
   sphereOverlaps.removeAllElements();
   for(int j = 0; j < sphereCount; j++){
       if(i != j){
    double dx = stx[i] - stx[j];
    double dy = sty[i] - sty[j];
    double dz = stz[i] - stz[j];
    double rr = str[i] + str[j];
    if(dx*dx + dy*dy + dz*dz < rr*rr){
        sphereOverlaps.add(j);
    }
       }
   }
   actuallyDrawSphere(stx[i], sty[i], stz[i],
        str[i],
        stransrgb.get(i),
        stransp.get(i));
      }
  }
     }
 }
    }
    /**
     * weirdSort -
     * Fast sorting mechanism of unknown origin.
     */
    private void indexSort(double sort[], int index[], int L, int R){
        double m = (sort[index[L]] + sort[index[R]])/2;
        int i = L;
        int j = R;
        int temp;
        do {
            while(sort[index[i]] < m) i++;
            while(sort[index[j]] > m) j--;
            if (i <= j){
                temp = index[i];
                index[i] = index[j];
                index[j] = temp;
                i++;
                j--;
            }
        } while(j >= i);
        if (L < j) indexSort(sort,index,L,j);
        if (R > i) indexSort(sort,index,i,R);
    }
    /** Local arrays for transforming tmesh coords. */
    private int xt[] = null;
    private int yt[] = null;
    private int zt[] = null;
    private int nxt[] = null;
    private int nyt[] = null;
    private int nzt[] = null;
    private int ut[] = null;
    private int vt[] = null;
    private char clipped[] = null;
    /**
     * Make sure we have enough room to transform
     * the points in this object.
     */
    private void ensureTransformCapacity(int n){
 if(xt == null || xt.length < n){
     //System.out.println("allocating transform buffers: " + n);
     xt = new int[n];
     yt = new int[n];
     zt = new int[n];
     nxt = new int[n];
     nyt = new int[n];
     nzt = new int[n];
     ut = new int[n];
     vt = new int[n];
     clipped = new char[n];
 }
    }
    /** Return the overall scale. */
    public double getOverallScale(){
        //return scale * zoom;
        return zoom * pixelWidth/(width*2.0);
    }
    public Matrix overallMatrix = new Matrix();
    public Matrix rotationMatrix = new Matrix();
    /** Build the overall transformation matrix. */
    public void buildOverallMatrix(){
 overallMatrix.setIdentity();
 if(center != null){
     overallMatrix.translate(-center.x, -center.y, -center.z);
 }
 overallMatrix.transform(rotationMatrix);
 double overallScale = getOverallScale();
 // negative for y scale flips the image upside down
 //overallMatrix.scale(overallScale, -overallScale, overallScale);
 //original
 overallMatrix.scale(overallScale, -overallScale, 1.0);
 //overallMatrix.translate(width/2, height/2, 0.0);
 overallMatrix.translate(pixelWidth/2, pixelHeight/2, 0.0);
    //clipDistance * overallScale);
    }
    /** Rotate the transform around the x axis. */
    public void rotateX(double degrees){
 rotationMatrix.rotateXdegrees(degrees);
    }
    /** Rotate the transform around the x axis. */
    public void rotateY(double degrees){
 rotationMatrix.rotateYdegrees(degrees);
    }
    /** Rotate the transform around the x axis. */
    public void rotateZ(double degrees){
 rotationMatrix.rotateZdegrees(degrees);
    }
    /** Apply a transform to a point. */
    public void applyTransform2(double x, double y, double z, int s[]){
        Matrix m = overallMatrix;
        double mx00 = m.x00, mx01 = m.x01, mx02 = m.x02;
        double mx10 = m.x10, mx11 = m.x11, mx12 = m.x12;
        double mx20 = m.x20, mx21 = m.x21, mx22 = m.x22;
        double mx30 = m.x30, mx31 = m.x31, mx32 = m.x32;
        double xx = x*mx00 + y*mx10 + z*mx20 + mx30 + 0.5;
        double yy = x*mx01 + y*mx11 + z*mx21 + mx31 + 0.5;
        double zz = x*mx02 + y*mx12 + z*mx22 + mx32;
        s[0] = (int)xx;
        s[1] = (int)yy;
        s[2] = (int)zz;
    }
    /** Apply a transform to a point. */
    public void applyTransform(double x, double y, double z, double s[]){
        Matrix m = overallMatrix;
        double mx00 = m.x00, mx01 = m.x01, mx02 = m.x02;
        double mx10 = m.x10, mx11 = m.x11, mx12 = m.x12;
        double mx20 = m.x20, mx21 = m.x21, mx22 = m.x22;
        double mx30 = m.x30, mx31 = m.x31, mx32 = m.x32;
        double xx = x*mx00 + y*mx10 + z*mx20 + mx30 + 0.5;
        double yy = x*mx01 + y*mx11 + z*mx21 + mx31 + 0.5;
        double zz = x*mx02 + y*mx12 + z*mx22 + mx32;
        s[0] = xx;
        s[1] = yy;
        s[2] = zz;
    }
    /** Apply a transform to a point. */
    public void transformNormal(double x, double y, double z, double s[]){
        Matrix m = rotationMatrix;
        double mx00 = m.x00, mx01 = m.x01, mx02 = m.x02;
        double mx10 = m.x10, mx11 = m.x11, mx12 = m.x12;
        double mx20 = m.x20, mx21 = m.x21, mx22 = m.x22;
        double mx30 = m.x30, mx31 = m.x31, mx32 = m.x32;
        double xx = x*mx00 + y*mx10 + z*mx20;
        double yy = x*mx01 + y*mx11 + z*mx21;
        double zz = x*mx02 + y*mx12 + z*mx22;
 // y-coordinate needs inverting for on screen
        s[0] = xx;
        s[1] = -yy;
        s[2] = zz;
    }
    /** Transform the coords of a tmesh. */
    private void transformObject(Tmesh tmesh){
 int np = tmesh.np;
 float xlocal[] = tmesh.x;
 float ylocal[] = tmesh.y;
 float zlocal[] = tmesh.z;
 float nxlocal[] = tmesh.nx;
 float nylocal[] = tmesh.ny;
 float nzlocal[] = tmesh.nz;
 float ulocal[] = tmesh.u;
 float vlocal[] = tmesh.v;
 Matrix m = overallMatrix;
 double mx00 = m.x00, mx01 = m.x01, mx02 = m.x02;
 double mx10 = m.x10, mx11 = m.x11, mx12 = m.x12;
 double mx20 = m.x20, mx21 = m.x21, mx22 = m.x22;
 double mx30 = m.x30, mx31 = m.x31, mx32 = m.x32;
 Matrix r = rotationMatrix;
 double rx00 = r.x00, rx01 = r.x01, rx02 = r.x02;
 double rx10 = r.x10, rx11 = r.x11, rx12 = r.x12;
 double rx20 = r.x20, rx21 = r.x21, rx22 = r.x22;
 double rx30 = r.x30, rx31 = r.x31, rx32 = r.x32;
 double x, y, z, xx, yy, zz;
 boolean transformNormals = false;
 double fnb = FFixedBits * NormalSamples;
 int i;
 ensureTransformCapacity(np);
 if(tmesh.style == Tmesh.TRIANGLES){
     transformNormals = true;
 }
 for(i = 0; i < np; i++){
     // first transform the coordinates
     x = xlocal[i];
     y = ylocal[i];
     z = zlocal[i];
     // we need to build in the scale by FFixedBits into the
     // overall matrix transformation, to save us
     // 6 multiplies below
     xx = x*mx00 + y*mx10 + z*mx20 + mx30 + 0.5;
     yy = x*mx01 + y*mx11 + z*mx21 + mx31 + 0.5;
     zz = x*mx02 + y*mx12 + z*mx22 + mx32;
     //System.out.print("zz " + zz);
     // we have to have pixel coordinate x and y values
     clipped[i] = 0;
     xt[i] = (int)(xx);
     yt[i] = (int)(yy);
     if(xt[i] < 0) clipped[i] |= XMinClip;
     else if(xt[i] >= pixelWidth) clipped[i] |= XMaxClip;
     if(yt[i] < 0) clipped[i] |= YMinClip;
     else if(yt[i] >= pixelHeight) clipped[i] |= YMaxClip;
     xt[i] <<= FixedBits;
     yt[i] <<= FixedBits;
     zt[i] = (int)(zz * ZFixedBits);
     if(debug){
  drawString(xlocal[i], ylocal[i], zlocal[i], 0.1, Color32.white, "" + i);
     }
     //System.out.println(" becomes " + zt[i]);
     if(zt[i] < zmin){
  zmin = zt[i];
     }
     if(zt[i] > zmax){
  zmax = zt[i];
     }
     if(zt[i] < backClip){
  clipped[i] |= ZMinClip;
     }else if(zt[i] > frontClip){
  clipped[i] |= ZMaxClip;
     }
     if(transformNormals && nxlocal != null){
  // now transform the normals
  x = nxlocal[i];
  y = nylocal[i];
  z = nzlocal[i];
  xx = x*rx00 + y*rx10 + z*rx20;
  yy = x*rx01 + y*rx11 + z*rx21;
  zz = x*rx02 + y*rx12 + z*rx22;
  xx = (xx * NormalSamples) + NormalSamples;
  if(xx < 0){
      xx = 0;
  }else if(xx > NormalSamples2){
      xx = NormalSamples2;
  }
  yy = (yy * NormalSamples) + NormalSamples;
  if(yy < 0){
      yy = 0;
  }else if(yy > NormalSamples2){
      yy = NormalSamples2;
  }
  nxt[i] = (int)(xx * FFixedBits);
  nyt[i] = (int)(yy * FFixedBits);
  nzt[i] = (int)(zz * FFixedBits);
  if(frontFaceOnly){
      if(nzt[i] < 0.0){
   clipped[i] |= NormalClip;
      }
  }
     }
 }
        if(false){
            for(int t = 0; t < tmesh.nt; t++){
                drawString(xlocal[tmesh.t0[t]],
                           ylocal[tmesh.t0[t]],
                           zlocal[tmesh.t0[t]], 0.1, Color32.white, "" + tmesh.t0[t]);
                drawString(xlocal[tmesh.t1[t]],
                           ylocal[tmesh.t1[t]],
                           zlocal[tmesh.t1[t]], 0.1, Color32.white, "" + tmesh.t1[t]);
                drawString(xlocal[tmesh.t2[t]],
                           ylocal[tmesh.t2[t]],
                           zlocal[tmesh.t2[t]], 0.1, Color32.white, "" + tmesh.t2[t]);
            }
        }
 // multiply the texture coordinates	
 if(texture != null && ulocal != null){
     double uscale = tmesh.getUScale();
     double vscale = tmesh.getVScale();
     double uoffset = tmesh.getUOffset();
     double voffset = tmesh.getVOffset();
     for(i = 0; i < np; i++){
  // apply u,v scales and offsets
  double utmp = (uscale * (ulocal[i] - uoffset))* 255;
  double vtmp = (vscale * (vlocal[i] - voffset))* 255;
  //if((int)utmp < 0) clipped[i] |= UMinClip;
  //if((int)utmp >= 255) clipped[i] |= UMaxClip;
  //if(utmp < 0.0) utmp = 0.0;
  //if(utmp > 255.0) utmp = 255.0;
  if((int)vtmp < 0) clipped[i] |= VMinClip;
  if((int)vtmp >= 255) clipped[i] |= VMaxClip;
  ut[i] = (int)(utmp * FFixedBits);
  vt[i] = (int)(vtmp * FFixedBits);
     }
 }
    }
    /* Temporary variables for triangle rendering. */
    /* Coordinates, normals, textures/rgb. */
    //private int AA[] = new int[10];
    //private int BB[] = new int[10];
    //private int CC[] = new int[10];
    //private int DD[] = new int[10];
    private Vertex vertexA = new Vertex();
    private Vertex vertexB = new Vertex();
    private Vertex vertexC = new Vertex();
    private Vertex vertexD = new Vertex();
    private Vertex vA = null;
    private Vertex vB = null;
    private Vertex vC = null;
    private Vertex vD = null;
    //private int A[] = null;
    //private int B[] = null;
    //private int C[] = null;
    //private int D[] = null;
    private int totalSpans = 0;
    private int spanLength = 0;
    private int spans[] = new int[100];
    private int v0, v1, v2, temp;
    private int components = 10;
    private boolean totallyOnScreen = false;
    /** The render mode for the current object. */
    private int renderMode = 0;
    /** The components of the rendermode. */
    private static final int ModeTransparent = 0x1;
    private static final int ModeTexture = 0x2;
    private static final int ModeTriangle = 0x4;
    private static final int ModeVertex = 0x8;
    private static final int ModeTextureTransparent = ModeTexture|ModeTransparent;
    private static final int ModeTriangleTransparent = ModeTriangle|ModeTransparent;
    private static final int ModeVertexTransparent = ModeVertex|ModeTransparent;
    /** Render a tmesh object. */
    private void renderObject(Tmesh tmesh){
 // only ca
 if(tmesh instanceof GraphicalObject){
     tmesh.render();
 }else{
     renderTmeshObject(tmesh);
 }
    }
    /** Render an actual tmesh object. */
    private void renderTmeshObject(Tmesh tmesh){
 if(tmesh.transparency == 0){
     return;
 }
 renderMode = 0;
 // check for transparency settings
 transparency = tmesh.transparency;
 transparent = (transparency != 0xff);
 if(transparency != 0xff){
     renderMode |= ModeTransparent;
 }
 // install the objects texture map if it has one
 texture = tmesh.texture;
 if(texture != null){
     renderMode |= ModeTexture;
 }
 frontFaceOnly = !tmesh.backface;
 if(tmesh.colorStyle == Tmesh.VertexColor){
     renderMode |= ModeVertex;
 }else if(tmesh.colorStyle == Tmesh.ObjectColor){
     setColor(tmesh.color);
 }else if((renderMode & ModeTexture) == 0){
     renderMode |= ModeTriangle;
 }
 // fix me
 //renderMode |= ModeTriangle;
 if(texture != null){
     phong = false;
 }else{
     phong = true;
 }
 transformObject(tmesh);
 if(tmesh.style == Tmesh.SPHERES){
     renderSphereObject(tmesh);
 }else if(tmesh.style == Tmesh.LINES){
     drawLineObject(tmesh);
 }else if(tmesh.style == Tmesh.TRIANGLES){
     renderTriangleObject(tmesh);
 }else if(tmesh.style == Tmesh.DOTS){
     renderDotObject(tmesh);
 }
 if(tmesh.spheres != null){
     renderSphereObject(tmesh.spheres);
 }
 if(tmesh.cylinders != null){
     renderCylinderObject(tmesh.cylinders);
 }
    }
    /** Depth sort the triangles. */
    private void depthSortTriangles(Tmesh tm){
 int tri[] = displayOrder;
 if(tm.nt == 0) return;
 depthSort(tri, tm, 0, tm.nt - 1);
    }
    private int triangleDepth(Tmesh tm, int t){
 int v0 = tm.t0[t];
 int v1 = tm.t1[t];
 int v2 = tm.t2[t];
 return -(zt[v0] + zt[v1] + zt[v2]);
    }
    private void depthSort(int tri[], Tmesh tm, int L, int R) {
 int m=(triangleDepth(tm, tri[L])+triangleDepth(tm, tri[R]))/2;
 int i=L;
 int j=R;
 int temp;
 do {
     while (triangleDepth(tm, tri[i])>m) i++;
     while (triangleDepth(tm, tri[j])<m) j--;
     if (i<=j) {
  temp=tri[i];
  tri[i]=tri[j];
  tri[j]=temp;
  i++;
  j--;
     }
 } while (j>=i);
 if (L<j) depthSort(tri, tm, L,j);
 if (R>i) depthSort(tri, tm, i,R);
    }
    /** Global display order. */
    private int displayOrder[] = null;
    /** Make sure we have the display list. */
    private void ensureDisplayList(Tmesh tmesh){
 int len = tmesh.nt;
 if(displayOrder == null ||
    displayOrder.length < len){
     displayOrder = new int[len];
 }
 for(int i = 0; i < len; i++){
     displayOrder[i] = i;
 }
    }
    private int zRange = -1;
    private int pixelShade = -1;
    /** Render a tmesh that is made of triangles. */
    private void renderTriangleObject(Tmesh tmesh){
 int triangles = tmesh.nt;
 int tri0[] = tmesh.t0;
 int tri1[] = tmesh.t1;
 int tri2[] = tmesh.t2;
 double shadowZscale = getOverallScale() / ZFixedBits;
 //double shadowZscale = getOverallScale();
 int i;
 ensureDisplayList(tmesh);
 if((renderMode & ModeTransparent) != 0){
     depthSortTriangles(tmesh);
 }
 if(!lightMapCalculated){
     calculateLightMap();
 }
 components = 10;
 if(phong != true){
     components = 10;
 }
 if((renderMode & ModeTexture) != 0){
     components = 10;
 }
 if((renderMode & ModeTriangle) != 0){
     colorTriangle = true;
     phong = false;
 }else{
     colorTriangle = false;
     phong = true;
 }
 if((renderMode & ModeVertex) != 0){
     components = 10;
 }
 zRange = (frontClip - backClip) >> 8;
 // override triangle colouring mode
 //colorTriangle = true;
 // this way does more correct clipping
 // back to front so you can see the back
 //for(int ii = 0; ii < triangles; ii++){
 // this does more interpretable transparency
 // front to back so you don't see internal 
 // features.
        int trianglesRendered = 0;
        for(int ii = triangles - 1; ii >= 0; ii--){
     //for(int ii = 0; ii < triangles; ii++){
     i = displayOrder[ii];
     v0 = tri0[i];
     v1 = tri1[i];
     v2 = tri2[i];
     clipTriangle = false;
     trianglesRendered++;
     if(shadowMode == ShadowsOn && triangles > 100000){
  if((trianglesRendered % 10000) == 0){
      FILE.out.print("%7d/", trianglesRendered);
      FILE.out.print("%d\n", triangles);
  }
     }
     /*
	     * Sutherland-Cohen clipping handles all aspects of clipping
	     * here, x,y,z texture and backface...
	     * Clever isn't it.
	     */
     totallyOnScreen =
  (clipped[v0] == 0 && clipped[v1] == 0 && clipped[v2] == 0);
     if(totallyOnScreen ||
        ((clipped[v0] & clipped[v1] & clipped[v2]) == 0) ||
        shadowMode == ShadowsAccumulate){
  /* Only special case is triangles that cross z-clip. */
  if((((clipped[v0] | clipped[v1] | clipped[v2]) &
       (ZMinClip|ZMaxClip)) != 0)){
      clipTriangle = true;
  }
  /* Order the points from minimum to maximum. */
  if (yt[v0] > yt[v1]) { temp = v0; v0 = v1; v1 = temp; }
  if (yt[v1] > yt[v2]) { temp = v1; v1 = v2; v2 = temp; }
  if (yt[v0] > yt[v1]) { temp = v0; v0 = v1; v1 = temp; }
  if(yt[v0] == yt[v2] && shadowMode != ShadowsAccumulate){
      continue;
  }
  vertexA.x = xt[v0]; vertexA.y = yt[v0]; vertexA.z = zt[v0];
  vertexA.nx = nxt[v0]; vertexA.ny = nyt[v0];
  vertexB.x = xt[v1]; vertexB.y = yt[v1]; vertexB.z = zt[v1];
  vertexB.nx = nxt[v1]; vertexB.ny = nyt[v1];
  vertexD.x = xt[v2]; vertexD.y = yt[v2]; vertexD.z = zt[v2];
  vertexD.nx = nxt[v2]; vertexD.ny = nyt[v2];
  if((renderMode & ModeVertex) != 0){
      vertexColor(vertexA, tmesh.vcolor[v0]);
      vertexColor(vertexB, tmesh.vcolor[v1]);
      vertexColor(vertexD, tmesh.vcolor[v2]);
  }
  if(texture != null){
      vertexA.u = ut[v0]; vertexA.v = vt[v0];
      vertexB.u = ut[v1]; vertexB.v = vt[v1];
      vertexD.u = ut[v2]; vertexD.v = vt[v2];
  }
  if(phong != true){
      // wrong ... needs doing in transform loop
      lightVertex(vertexA);
      lightVertex(vertexB);
      lightVertex(vertexD);
  }
  if(colorTriangle){
      if(tmesh.tcolor[i] != 0){
   triangleColor = tmesh.tcolor[i];
      }else{
   triangleColor = debugColor[i%debugColor.length];
      }
      triangleColorR = Color32.getRed(triangleColor);
      triangleColorG = Color32.getGreen(triangleColor);
      triangleColorB = Color32.getBlue(triangleColor);
  }
  if(shadowMode == ShadowsOn){
      applyTransform(tmesh.x[v0], tmesh.y[v0], tmesh.z[v0], cx0);
      applyTransform(tmesh.x[v1], tmesh.y[v1], tmesh.z[v1], cx1);
      applyTransform(tmesh.x[v2], tmesh.y[v2], tmesh.z[v2], cx2);
      ShadowCache.prepareTriangleCacheList(cx0[0], cx0[1], cx0[2] * getOverallScale(),
            cx1[0], cx1[1], cx1[2] * getOverallScale(),
            cx2[0], cx2[1], cx2[2] * getOverallScale(),
        transparent);
      transformNormal(tmesh.nx[v0], tmesh.ny[v0], tmesh.nz[v0], nx0);
      transformNormal(tmesh.nx[v1], tmesh.ny[v1], tmesh.nz[v1], nx1);
      transformNormal(tmesh.nx[v2], tmesh.ny[v2], tmesh.nz[v2], nx2);
      if(texture != null){
   textureMap = texture.pixels;
      }
      renderAccurateTriangle();
      continue;
  }else if(shadowMode == ShadowsAccumulate){
      applyTransform(tmesh.x[v0], tmesh.y[v0], tmesh.z[v0], cx0);
      applyTransform(tmesh.x[v1], tmesh.y[v1], tmesh.z[v1], cx1);
      applyTransform(tmesh.x[v2], tmesh.y[v2], tmesh.z[v2], cx2);
      cx0[2] *= getOverallScale();
      cx1[2] *= getOverallScale();
      cx2[2] *= getOverallScale();
      ShadowCache.addTriangleToCacheList(cx0[0], cx0[1], cx0[2],
             cx1[0], cx1[1], cx1[2],
             cx2[0], cx2[1], cx2[2],
             transparency);
      // don't return, need to keep processing other triangles in this loop
      continue;
  }else if(shadowMode == ShadowsOff){
      if(false){
   applyTransform(tmesh.x[v0], tmesh.y[v0], tmesh.z[v0], cx0);
   applyTransform(tmesh.x[v1], tmesh.y[v1], tmesh.z[v1], cx1);
   applyTransform(tmesh.x[v2], tmesh.y[v2], tmesh.z[v2], cx2);
   transformNormal(tmesh.nx[v0], tmesh.ny[v0], tmesh.nz[v0], nx0);
   transformNormal(tmesh.nx[v1], tmesh.ny[v1], tmesh.nz[v1], nx1);
   transformNormal(tmesh.nx[v2], tmesh.ny[v2], tmesh.nz[v2], nx2);
   renderAccurateTriangle();
      }else{
   renderTriangle();
      }
  }
     }
 }
    }
    private double tuv[] = new double[3];
    private double eye[] = new double[3];
    private double eyedir[] = new double[3];
    private int textureMap[] = null;
    private int triangleRaysCast = 0;
    private int triangleRaysIntersected = 0;
    /** Analytical triangle rendering for shadow casting. */
    private void renderAccurateTriangle(){
 // get bounds of triangle
 int pxmin = 1+(int)cx0[0];
 if(cx1[0] < pxmin) pxmin = (int)cx1[0];
 if(cx2[0] < pxmin) pxmin = (int)cx2[0];
 int pymin = 1+(int)cx0[1];
 if(cx1[1] < pymin) pymin = (int)cx1[1];
 if(cx2[1] < pymin) pymin = (int)cx2[1];
 int pxmax = (int)cx0[0];
 if(cx1[0] > pxmax) pxmax = (int)cx1[0];
 if(cx2[0] > pxmax) pxmax = (int)cx2[0];
 int pymax = (int)cx0[1];
 if(cx1[1] > pymax) pymax = (int)cx1[1];
 if(cx2[1] > pymax) pymax = (int)cx2[1];
 if(pxmin < 0) pxmin = 0;
 if(pymin < 0) pymin = 0;
 if(pxmax >= pixelWidth) pxmax = pixelWidth - 1;
 if(pymax >= pixelHeight) pymax = pixelHeight - 1;
 eyedir[0] = 0.0;
 eyedir[1] = 0.0;
 eyedir[2] = 1.0;
 eye[2] = back;
 boolean renderedScanline = false;
 // main loop over triangles
 for(int y = pymin; y <= pymax; y++){
     eye[1] = y;
     renderedScanline = false;
     for(int x = pxmin; x <= pxmax; x++){
  eye[0] = x;
  triangleRaysCast++;
  if(ShadowCache.intersect_triangle(eye, eyedir, cx0, cx1, cx2, tuv) == 1){
      if(tuv[0] >= 0.0){
   int i = INDEX(x, y);
   double z = eye[2] + tuv[0] * eyedir[2];
   int iz = (int)(z * ZFixedBits);
   triangleRaysIntersected++;
   if(iz >= backClip && iz <= frontClip && iz >= zbuffer[i]){
       double oneuv = 1. - tuv[1] - tuv[2];
       double nx = oneuv * nx0[0] + tuv[1] * nx1[0] + tuv[2] * nx2[0];
       double ny = oneuv * nx0[1] + tuv[1] * nx1[1] + tuv[2] * nx2[1];
       double nz = oneuv * nx0[2] + tuv[1] * nx1[2] + tuv[2] * nx2[2];
       renderPixel(x, y, z, iz, nx, ny, nz, tuv, i);
   }
   renderedScanline = true;
      }else{
   if(renderedScanline){
       break;
   }
      }
  }
     }
 }
    }
    private int rp_drgb[] = new int[3];
    private int rp_srgb[] = new int[3];
    private int rp_shadowrgb[] = new int[3];
    /** Perform rendering operations on a single pixel. */
    private void renderPixel(double x, double y, double z, int iz,
        double nx, double ny, double nz, double tuv[],
        int ipix){
 boolean inside = false;
 // flip normals round if pixel normal
 // faces backwards. This makes the inside of surfaces
 // shade correctly (but does cause some artifacts at the
 // inversion point).
 if(nz < -0.05){
     inside = true;
     nx = -nx;
     ny = -ny;
     nz = -nz;
 }
 int inx = (int)(NormalSamples + nx*NormalSamples);
 int iny = (int)(NormalSamples - ny*NormalSamples);
 int lutID = inx + (iny<<NormalBits);
 int pcolor = 0xff00ff;
 // form the base color
 // either
 // color comes from tmesh
 // color comes from triangle
 // color comes from texturemap
 if((renderMode & ModeTriangle) != 0){
     //pcolor = Color32.multiply(triangleColor, diffuseMap[lutID]);
     pcolor = triangleColor;
 }else if((renderMode & ModeVertex) != 0){
     double oneuv = 1. - tuv[1] - tuv[2];
     if(texture != null){
  int v = (int)(oneuv * vertexA.v + tuv[1] * vertexB.v + tuv[2] * vertexD.v);
  if(v < 0 || v >= (256*FFixedBits)){
      return;
  }
     }
     int r = (int)(oneuv * vertexA.r + tuv[1] * vertexB.r + tuv[2] * vertexD.r);
     int g = (int)(oneuv * vertexA.g + tuv[1] * vertexB.g + tuv[2] * vertexD.g);
     int b = (int)(oneuv * vertexA.b + tuv[1] * vertexB.b + tuv[2] * vertexD.b);
     pcolor = Color32.pack(r>>FixedBits, g>>FixedBits, b>>FixedBits);
 }else if((renderMode & ModeTexture) != 0){
     double oneuv = 1. - tuv[1] - tuv[2];
     int u = (int)(oneuv * vertexA.u + tuv[1] * vertexB.u + tuv[2] * vertexD.u);
     int v = (int)(oneuv * vertexA.v + tuv[1] * vertexB.v + tuv[2] * vertexD.v);
     int ucoord = (u>>FixedBits);
     int vcoord = (v>>FixedBits);
     if(ucoord < 0) ucoord = 0;
     else if(ucoord > 255) ucoord = 255;
     if(vcoord < 0 || vcoord >= 256){
  // v always clips
  return;
     }
     // look up the texture color
     pcolor = textureMap[ucoord+(vcoord<<8)];
     // black is always skipped in the texture
     if((pcolor&0xffffff) == 0){
  return;
     }
 }else{
     // its the whole tmesh is one color
     pcolor = color;
 }
 // ok we have to draw the pixel, update the z-buffer
 zbuffer[ipix] = iz;
 // ambient
 //int c = Color32.add(pcolor, ambient);
 // diffuse map contains ambient component already?
 int c = pcolor;
 if(inside){
     // point on inside of surface gets
     // no specular highlight
     // and is drawn at lower intensity
     c = Color32.scale(c, 200);
     //nx = -nx;
     //ny = -ny;
     //nz = -nz;
 }
 // figure out if we need shadowin
 boolean shadowed = false;
 if(shadowMode == ShadowsOn &&
    (ShadowCache.selfShadowed(nx, ny, nz, cosWrapAngle) ||
     ShadowCache.pointShadowed(x, y, z * getOverallScale()))){
     shadowed = true;
 }
 if(shadowed == false){
     c = Color32.multiply(c, diffuseMap[lutID]);
     //c = Color32.multiply(c, rp_drgb[0], rp_drgb[1], rp_drgb[2]);
     int s = highlightMap[lutID];
     // if we need transparency put it in here
     // assume that render order is ok
     if((renderMode & ModeTransparent) != 0){
  //System.out.println("transparency " + transparency);
  c = Color32.blend(c, pbuffer[ipix], transparency);
     }
     c = Color32.add(c, s);
     //c = Color32.add(c, Color32.pack(rp_srgb[0], rp_srgb[1], rp_srgb[2]));
 } else{
     c = Color32.multiply(c, shadowMap[lutID]);
     // if we need transparency put it in here
     // assume that render order is ok
     if((renderMode & ModeTransparent) != 0){
     //System.out.println("transparency " + transparency);
  c = Color32.blend(c, pbuffer[ipix], transparency);
     }
     //c = Color32.multiply(c, rp_shadowrgb[0], rp_shadowrgb[1], rp_shadowrgb[2]);
 }
 // apply fogging.
 int zscale = (iz - backClip)/zRange;
 int shade = depthScale[zscale];
 c = Color32.blend(c, background, shade);
 // store the color
 pbuffer[ipix] = c;
    }
    /** An irregular shapped triangle. */
    private static final int Irregular = 0;
    /** A flat topped triangle. */
    private static final int FlatTop = 1;
    /** A flat bottomed triangle. */
    private static final int FlatBottom = 2;
    /** Render a triangle that is a component of a tmesh. */
    private void renderTriangle(){
 int i;
 int triCase = Irregular;
 //System.out.println("phong " + phong);
 //System.out.println("texture " + texture);
 if(vertexA.y == vertexB.y){
     triCase = FlatTop;
 }else if(vertexD.y == vertexB.y){
     triCase = FlatBottom;
 }
 /* Interpolate intersection point. */
 float t = (float)(vertexB.y - vertexA.y) / (vertexD.y - vertexA.y);
        vertexC.x = (int)(vertexA.x + t * (vertexD.x - vertexA.x));
        vertexC.y = (int)(vertexA.y + t * (vertexD.y - vertexA.y));
        vertexC.z = (int)(vertexA.z + t * (vertexD.z - vertexA.z));
        vertexC.nx = (int)(vertexA.nx + t * (vertexD.nx - vertexA.nx));
        vertexC.ny = (int)(vertexA.ny + t * (vertexD.ny - vertexA.ny));
        vertexC.u = (int)(vertexA.u + t * (vertexD.u - vertexA.u));
        vertexC.v = (int)(vertexA.v + t * (vertexD.v - vertexA.v));
        vertexC.r = (int)(vertexA.r + t * (vertexD.r - vertexA.r));
        vertexC.g = (int)(vertexA.g + t * (vertexD.g - vertexA.g));
        vertexC.b = (int)(vertexA.b + t * (vertexD.b - vertexA.b));
 /* Fix x-coordinate of c to make sure we don't generate holes. */
 int xa = vertexA.x>>FixedBits;
 int xd = vertexD.x>>FixedBits;
 int dx = xd - xa;
 if(dx > 0){
     int ya = vertexA.y>>FixedBits;
     int yd = vertexD.y>>FixedBits;
     int dy = yd - ya;
     if(dy > 0){
  int dxp = (vertexD.x - vertexA.x)/dy;
  int yb = vertexB.y >> FixedBits;
  vertexC.x = vertexA.x + (yb-ya) * dxp;
     }
 }
 // swap vertexB and vertexC round to simplify rest of renderer.
 if(vertexB.x >= vertexC.x){
     Vertex tt = vertexB;
     vertexB = vertexC;
     vertexC = tt;
 }
 /*
	 * Draw the top and bottom half of the triangle.
	 * Order depends on whether b is to left of c or not.
	 */
 if(!clipTriangle){
     switch(renderMode){
     case 0:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleP();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleP();
  break;
     case ModeTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTrianglePTrans();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTrianglePTrans();
  break;
     case ModeTriangle:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTri();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTri();
  break;
     case ModeVertex:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleVertex();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleVertex();
  break;
     case ModeVertexTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleVertexTrans();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleVertexTrans();
  break;
     case ModeTextureTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTTrans();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTTrans();
  break;
     case ModeTriangleTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTriTrans();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTriTrans();
  break;
     case ModeTexture:
  if(totallyOnScreen){
      vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTF();
      vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTF();
  }else{
      vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleT();
      vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleT();
  }
  break;
     default:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleUniversal();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleUniversal();
  break;
  //System.out.println("unhandlend triangle mode "+renderMode);
  //break;
     }
 }else{
     switch(renderMode){
     case 0:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTrianglePC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTrianglePC();
  break;
     case ModeTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTrianglePTransC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTrianglePTransC();
  break;
     case ModeTriangle:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTriC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTriC();
  break;
     case ModeVertex:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleVertexC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleVertexC();
  break;
     case ModeTextureTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTTransC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTTransC();
  break;
     case ModeTriangleTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTriTransC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTriTransC();
  break;
     case ModeVertexTransparent:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleVertexTransC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleVertexTransC();
  break;
     case ModeTexture:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleTC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleTC();
  break;
     default:
  vA = vertexA; vB = vertexA; vC = vertexB; vD = vertexC; renderTriangleUniversalC();
  vA = vertexB; vB = vertexC; vC = vertexD; vD = vertexD; renderTriangleUniversalC();
  break;
     }
 }
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleUniversal(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 //Oops no defintion for those settings!
 c = 0xff00ff;
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleUniversalC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 //Oops no defintion for those settings!
 c = 0xff00ff;
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleVertex(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = Color32.pack(r>>FixedBits, g>>FixedBits, b>>FixedBits);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleTri(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = triangleColor;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Phong shaded rasterizer with clipping. */
    private void renderTriangleTriC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = triangleColor;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Phong shaded rasterizer with clipping. */
    private void renderTriangleVertexC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = Color32.pack(r>>FixedBits, g>>FixedBits, b>>FixedBits);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleTriTrans(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = triangleColor;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleVertexTrans(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = Color32.pack(r>>FixedBits, g>>FixedBits, b>>FixedBits);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer with clipping. */
    private void renderTriangleTriTransC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = triangleColor;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Phong shaded rasterizer with clipping. */
    private void renderTriangleVertexTransC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int rL = vA.r;
int gL = vA.g;
int bL = vA.b;
int rR = vB.r;
int gR = vB.g;
int bR = vB.b;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int drL = (vC.r - vA.r) / dy;
int dgL = (vC.g - vA.g) / dy;
int dbL = (vC.b - vA.b) / dy;
int drR = (vD.r - vB.r) / dy;
int dgR = (vD.g - vB.g) / dy;
int dbR = (vD.b - vB.b) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    rL -= drL * ymin;
    gL -= dgL * ymin;
    bL -= dbL * ymin;
    rR -= drR * ymin;
    gR -= dgR * ymin;
    bR -= dbR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int r = rL;
    int g = gL;
    int b = bL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 dr = (rR - rL) / dx;
 dg = (gR - gL) / dx;
 db = (bR - bL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 r -= dr * ixL;
 g -= dg * ixL;
 b -= db * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = Color32.pack(r>>FixedBits, g>>FixedBits, b>>FixedBits);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      r += dr;
      g += dg;
      b += db;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    rL += drL;
    gL += dgL;
    bL += dbL;
    rR += drR;
    gR += dgR;
    bR += dbR;
}
    }
    /** Phong shaded rasterizer. */
    private void renderTriangleP(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = cmap[lookup];
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Transparent texture shaded rasterizer. */
    private void renderTriangleTTrans(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
/* The texture map. */
int tmap[] = null;
if(texture != null){
    tmap = texture.pixels;
}
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 int ucoord = (u>>FixedBits);
 int vcoord = (v>>FixedBits);
 // clamp u into range
 if(ucoord < 0) ucoord = 0;
 else if(ucoord > 255) ucoord = 255;
 // clip v if outside of range 0-255
 if(vcoord < 0 || vcoord >= 256) continue;
 c = tmap[ucoord+(vcoord<<8)];
 // black is regarded as transparent colour in texture maps
 if((c & 0xffffff) == 0) continue;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
}
    }
    /** Transparent shaded rasterizer. */
    private void renderTrianglePTrans(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = cmap[lookup];
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Transparent shaded rasterizer with clipping. */
    private void renderTrianglePTransC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = cmap[lookup];
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Transparent texture shaded rasterizer with clipping. */
    private void renderTriangleTTransC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
/* The texture map. */
int tmap[] = null;
if(texture != null){
    tmap = texture.pixels;
}
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 int ucoord = (u>>FixedBits);
 int vcoord = (v>>FixedBits);
 // clamp u into range
 if(ucoord < 0) ucoord = 0;
 else if(ucoord > 255) ucoord = 255;
 // clip v if outside of range 0-255
 if(vcoord < 0 || vcoord >= 256) continue;
 c = tmap[ucoord+(vcoord<<8)];
 // black is regarded as transparent colour in texture maps
 if((c & 0xffffff) == 0) continue;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // do the transparency
 // according to what is already in the buffer
 c = Color32.blend(c, px[i], transparency);
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
}
    }
    /** Phong shaded rasterizer with clipping. */
    private void renderTrianglePC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 c = cmap[lookup];
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
}
    }
    /** Texture based rasterizer. */
    private void renderTriangleT(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
/* The texture map. */
int tmap[] = null;
if(texture != null){
    tmap = texture.pixels;
}
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 int ucoord = (u>>FixedBits);
 int vcoord = (v>>FixedBits);
 // clamp u into range
 if(ucoord < 0) ucoord = 0;
 else if(ucoord > 255) ucoord = 255;
 // clip v if outside of range 0-255
 if(vcoord < 0 || vcoord >= 256) continue;
 c = tmap[ucoord+(vcoord<<8)];
 // black is regarded as transparent colour in texture maps
 if((c & 0xffffff) == 0) continue;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
}
    }
    /** Faster texture based rasterizer. */
    private void renderTriangleTF(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
/* The texture map. */
int tmap[] = null;
if(texture != null){
    tmap = texture.pixels;
}
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z > zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 int ucoord = (u>>FixedBits);
 int vcoord = (v>>FixedBits);
 // clamp u into range
 if(ucoord < 0) ucoord = 0;
 else if(ucoord > 255) ucoord = 255;
 // clip v if outside of range 0-255
 if(vcoord < 0 || vcoord >= 256) continue;
 c = tmap[ucoord+(vcoord<<8)];
 // black is regarded as transparent colour in texture maps
 if((c & 0xffffff) == 0) continue;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
}
    }
    /** Texture based rasterizer with clipping. */
    private void renderTriangleTC(){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
// this file is included into Renderer.j to generate the different
// styles of triangle filling code
// need to round to nearest pixel
int ymin = ((vA.y+(1<<(FixedBits-1)))>>FixedBits);
int ymax = ((vC.y+(1<<(FixedBits-1)))>>FixedBits);
if (ymax < 0 || ymin >= pixelHeight){
    return;
}
int dy = ymax - ymin;
if (dy <= 0){
    return;
}
/* Local references to the pixel and z buffer. */
int zb[] = zbuffer, px[] = pbuffer;
/* Local references to the diffuse and specular lookup tables. */
int smap[] = highlightMap, dmap[] = diffuseMap, imap[] = intensityMap;
/* The texture map. */
int tmap[] = null;
if(texture != null){
    tmap = texture.pixels;
}
// local references to the renderer size
int lw = pixelWidth;
int lh = pixelHeight;
/* The color map. */
int cmap[] = colorMap;
// left side and right side
int xL = vA.x;
int zL = vA.z;
int xR = vB.x;
int zR = vB.z;
int nxL = vA.nx;
int nyL = vA.ny;
int nxR = vB.nx;
int nyR = vB.ny;
int uL = vA.u;
int vL = vA.v;
int uR = vB.u;
int vR = vB.v;
int dxL = (vC.x - vA.x) / dy;
int dzL = (vC.z - vA.z) / dy;
int dxR = (vD.x - vB.x) / dy;
int dzR = (vD.z - vB.z) / dy;
int dnxL = (vC.nx - vA.nx) / dy;
int dnyL = (vC.ny - vA.ny) / dy;
int dnxR = (vD.nx - vB.nx) / dy;
int dnyR = (vD.ny - vB.ny) / dy;
int duL = (vC.u - vA.u) / dy;
int dvL = (vC.v - vA.v) / dy;
int duR = (vD.u - vB.u) / dy;
int dvR = (vD.v - vB.v) / dy;
if (ymin < 0) {
    xL -= dxL * ymin;
    zL -= dzL * ymin;
    xR -= dxR * ymin;
    zR -= dzR * ymin;
    nxL -= dnxL * ymin;
    nyL -= dnyL * ymin;
    nxR -= dnxR * ymin;
    nyR -= dnyR * ymin;
    uL -= duL * ymin;
    vL -= dvL * ymin;
    uR -= duR * ymin;
    vR -= dvR * ymin;
    ymin = 0;
}
if(lh < ymax){
    ymax = lh;
}
for (int y = ymin ; y < ymax ; y++) {
  int dz = 0, dr = 0, dg = 0, db = 0;
  int dnx = 0, dny = 0, du = 0, dv = 0;
    int ixL = ((xL+(1<<(FixedBits-1)))>>FixedBits);
    int ixR = ((xR+(1<<(FixedBits-1)))>>FixedBits);
    int dx = ixR - ixL;
    int z = zL;
    int nx = nxL;
    int ny = nyL;
    int u = uL;
    int v = vL;
    if (dx > 0) {
 dz = (zR - zL) / dx;
 dnx = (nxR - nxL) / dx;
 dny = (nyR - nyL) / dx;
 du = (uR - uL) / dx;
 dv = (vR - vL) / dx;
    }
    if (ixL < 0) {
 z -= dz * ixL;
 nx -= dnx * ixL;
 ny -= dny * ixL;
 u -= du * ixL;
 v -= dv * ixL;
 ixL = 0;
    }
    if(ixR > lw){
 ixR = lw;
    }
    // index of first pixel on scanline
    int i = ixL + lw * y;
    //for (ix = ixL ; ix < ixR ; ix++) {
    for (; ixL < ixR ; ixL++) {
      if((z < frontClip && z >= zb[i])){
 int c;
 // here is the shading part of the process
 // calculate normal index into shading lookup tables
 int lookup = (((nx)>>FixedBits) + (((ny)>>FixedBits)<<NormalBits));
 // form the base colour depending on the mode
 // we are rendering the object in
 int ucoord = (u>>FixedBits);
 int vcoord = (v>>FixedBits);
 // clamp u into range
 if(ucoord < 0) ucoord = 0;
 else if(ucoord > 255) ucoord = 255;
 // clip v if outside of range 0-255
 if(vcoord < 0 || vcoord >= 256) continue;
 c = tmap[ucoord+(vcoord<<8)];
 // black is regarded as transparent colour in texture maps
 if((c & 0xffffff) == 0) continue;
 c = Color32.multiply(c, dmap[lookup]);
 // ok, the pixel survived, store the z-coordinate
 zb[i] = z;
 // finally add in specular highlights
 // highlights get done after transparency so that the highlight doesn't fade
 int s = smap[lookup];
 c = Color32.add(c, s);
 // depth cue according to distance in the scene
 int zscale = (z - backClip)/zRange;
 int shade = depthScale[zscale];
 px[i] = Color32.blend(c, background, shade);
      }
      /* Increment z and normals across scan line. */
      z += dz;
      nx += dnx;
      ny += dny;
      u += du;
      v += dv;
      i++;
    }
    /* Increment left and right edges and normals down triangle. */
    xL += dxL;
    zL += dzL;
    xR += dxR;
    zR += dzR;
    nxL += dnxL;
    nyL += dnyL;
    nxR += dnxR;
    nyR += dnyR;
    uL += duL;
    vL += dvL;
    uR += duR;
    vR += dvR;
}
    }
    private Tmesh cylinder = new Tmesh();
    private double cx0[] = new double[3];
    private double cx1[] = new double[3];
    private double cx2[] = new double[3];
    private double nx0[] = new double[3];
    private double nx1[] = new double[3];
    private double nx2[] = new double[3];
    private double ray1[] = new double[3];
    private double ray2[] = new double[3];
    private double i1[] = new double[3];
    private double i2[] = new double[3];
    private int scx1[] = new int[3];
    private int scx2[] = new int[3];
    private double afT[] = new double[2];
    private double cylPoint[] = new double[3];
    private double cylNormal[] = new double[3];
    /** Draw a cylinder. */
    public void drawCylinder(double x1, double y1, double z1,
        double x2, double y2, double z2,
        int rgb1, int rgb2, double r){
 if(rgb1 != rgb2){
     double xm = 0.5 *(x1 + x2);
     double ym = 0.5 *(y1 + y2);
     double zm = 0.5 *(z1 + z2);
     drawCylinderEnd(x1, y1, z1, xm, ym, zm, rgb1, r, false);
     drawCylinderEnd(x2, y2, z2, xm, ym, zm, rgb2, r, false);
 }else{
     drawCylinderEnd(x1, y1, z1, x2, y2, z2, rgb1, r, true);
 }
    }
    /** Draw a cylinder end. */
    private void drawCylinderEnd(double x1, double y1, double z1,
     double x2, double y2, double z2,
     int rgb1, double r, boolean top){
 double overallScale = getOverallScale();
 double overallScale1 = 1./getOverallScale();
 double rt = overallScale * r;
 /*
	if(!lightMapCalculated){
	    calculateLightMap();
	}

	color = Color32.white;

	if(!colorInitialised){
	    initialiseColor();
	}
	*/
 applyTransform(x1, y1, z1, cx1);
 applyTransform(x2, y2, z2, cx2);
 // world coord radius (not transformed)
 if(cx1[2] + r < back && cx2[2] + r < back) return;
 if(cx1[2] - r > front && cx2[2] - r > front) return;
 int rgb1shade = rgb1;
 if(background == Color32.black){
     rgb1shade = depthCueShadedColor(rgb1, (int)(0.5*(cx1[2]+cx2[2])*ZFixedBits));
 }
 //int rgb1shade = rgb1;
 // need to make z be same scale as x,y for the intersections
 cx1[2] *= overallScale;
 cx2[2] *= overallScale;
 //System.out.println("cx1[2] " + cx1[2]);
 //System.out.println("cx2[2] " + cx2[2]);
 Geometry.rayCapsuleIntInit(cx1, cx2, rt, front - back);
 // XXX
 // don't move this from here, it causes a problem with
 // the JIT in jdk1.1.8. Some sort of memory overwrite error
 // we have to draw it...
 if(shadowMode == ShadowsOn){
     // form bounding sphere
     ShadowCache.prepareCylinderCacheList(cx1[0], cx1[1], cx1[2],
       cx2[0], cx2[1], cx2[2],
       rt);
 }else if(shadowMode == ShadowsAccumulate){
     ShadowCache.addCylinderToCacheList(cx1[0], cx1[1], cx1[2] ,
            cx2[0], cx2[1], cx2[2],
            rt);
     return;
 }
 //int pxmin = (int)Math.min(cx1[0] - rt, cx2[0] - rt);
 //int pxmax = (int)Math.max(cx1[0] + rt + 1, cx2[0] + rt + 1);
 //int pymin = (int)Math.min(cx1[1] - rt, cx2[1] - rt);
 //int pymax = (int)Math.max(cx1[1] + rt + 1, cx2[1] + rt + 1);
 int pxmin = (int)(cx1[0]); if(cx2[0] < pxmin) pxmin = (int)cx2[0];
 pxmin -= rt + 3;
 int pxmax = (int)(cx1[0]); if(cx2[0] > pxmax) pxmax = (int)cx2[0];
 pxmax += rt + 3;
 int pymin = (int)(cx1[1]); if(cx2[1] < pymin) pymin = (int)cx2[1];
 pymin -= rt + 3;
 int pymax = (int)(cx1[1]); if(cx2[1] > pymax) pymax = (int)cx2[1];
 pymax += rt + 3;
 if(pxmin > pixelWidth || pymin > pixelHeight || pxmax < 0 || pymax < 0){
     return;
 }
 if(pxmin < 0) pxmin = 0;
 if(pymin < 0) pymin = 0;
 if(pxmax > pixelWidth) pxmax = pixelWidth;
 if(pymax > pixelHeight) pymax = pixelHeight;
 ray1[2] = back;
 ray2[2] = front;
 int pb[] = pbuffer;
 int zb[] = zbuffer;
 int zeroSpecular = 0;
 int nonZeroSpecular = 0;
 //rgb1shade = Color32.add(rgb1shade, ambient);
 for(int j = pymin; j < pymax; j++){
     ray1[1] = ray2[1] = j;
     int px = INDEX(pxmin, j);
     int lastIntCount = 0;
     for(int i = pxmin; i < pxmax; i++){
  ray1[0] = ray2[0] = i;
  int intCount =
      Geometry.rayCapsuleInt(ray1, ray2, cylPoint, cylNormal, top);
  if(intCount > 0){
      double zpos = cylPoint[2]*overallScale1;
      int izpos = (int)(zpos *ZFixedBits);
      int c = 0;
      if(izpos > zb[px] && izpos > backClip && izpos < frontClip){
   int inx =
       (int)(NormalSamples + cylNormal[0]*NormalSamples);
   int iny =
       (int)(NormalSamples - cylNormal[1]*NormalSamples);
   int lutID = inx + (iny<<NormalBits);
   if(shadowMode == ShadowsOn){
       if(ShadowCache.pointShadowed(i, j, zpos*overallScale )){
       //c = (c >> 1) & 0x7f7f7f;
    c = Color32.multiply(rgb1shade, shadowMap[lutID]);
       }else{
    int s = highlightMap[lutID];
    c = Color32.multiply(rgb1shade, diffuseMap[lutID]);
    c = Color32.add(c, s);
       }
   }else{
       int s = highlightMap[lutID];
       c = Color32.multiply(rgb1shade, diffuseMap[lutID]);
       c = Color32.add(c, s);
   }
   if(background != Color32.black){
       c = depthCueShadedColor(c, izpos);
   }
   pb[px] = c;
   zb[px] = izpos;
      }
  }
  //if(intCount == 0 && lastIntCount != 0){
  //    break;
  //}
  lastIntCount = intCount;
  px++;
     }
 }
 //System.out.println("sqrtCount "+ Geometry.sqrtCount);
 //System.out.println("one hit " + oneHit);
 //System.out.println("two hit " + twoHit);
    }
    /** The sphere colors. */
    private int sphereColor[] = null;
    /** The sphere zvalues. */
    private int sphereZ[] = null;
    /** Maximum number of sphere cache entries. */
    private static final int MaxCache = 32;
    private int szCache[][] = new int[MaxCache][];
    private int scCache[][] = new int[MaxCache][];
    private int sradius[] = new int[MaxCache];
    private int scolor[] = new int[MaxCache];
    private int cacheCount = 0;
    /** Generate the sphere bitmap for this radius. */
    private void generateSphereBitmap(double rorig, double rsd, int rgb){
 //Log.info("rorig %7.3f", rorig);
 //Log.info("rsd   %7.3f", rsd);
 if(!lightMapCalculated){
     calculateLightMap();
 }
 int rs = (int)rsd;
 //Log.info("rs    %d", rs);
 int r2 = rs * rs;
 int pixel = 0;
 int lutID;
 int c, s;
 int dmap[] = diffuseMap;
 int smap[] = highlightMap;
 sphereColor = new int[4 * rs * rs];
 sphereZ = new int[4 * rs * rs];
 //System.out.println("sphere memory " + (2 * sphereZ.length * 4));
 for(int iy = -rs; iy < rs; iy++){
     double ny = (double)iy/rs;
     int iny = (int)(NormalSamples + ny * NormalSamples);
     int iy2 = iy * iy;
     for(int ix = -rs; ix < rs; ix++){
  int ix2 = ix * ix;
  double nx = (double)ix/rs;
  //if(ix2 + iy2 < r2){
  // makes spheres `rounder'
  if(ny*ny + nx*nx < 0.97){
      int inx = (int)(NormalSamples + nx * NormalSamples);
      int nz =
   (int)(rorig * Math.sqrt(1. - (ny*ny + nx*nx))*ZFixedBits);
      sphereZ[pixel] = nz;
      lutID = inx + (iny<<NormalBits);
      c = Color32.multiply(rgb, dmap[lutID]);
      s = smap[lutID];
      sphereColor[pixel] = Color32.add(c, s);
  }else{
      sphereZ[pixel] = 0;
  }
  pixel++;
     }
 }
 // if there is a cache slot add it
 int slot = 0;
 if(cacheCount < MaxCache){
     slot = cacheCount;
 }else{
     // random slot on basis of radius
     slot = rs % MaxCache;
 }
 szCache[slot] = sphereZ;
 scCache[slot] = sphereColor;
 sradius[slot] = rs;
 scolor[slot] = rgb;
 if(cacheCount < MaxCache){
     cacheCount++;
 }
    }
    /** Draw a sphere. */
    public void drawSphere(double x, double y, double z, double r, int rgb){
 if(!lightMapCalculated){
     calculateLightMap();
 }
 drawSphere(x, y, z, r, rgb, 255);
    }
    public void drawSphere(double x, double y, double z, double r, int rgb, int transp){
 if(transp == 255){
     actuallyDrawSphere(x, y, z, r, rgb, transp);
 }else{
     cacheTransparentSphere(x, y, z, r, rgb, transp);
 }
    }
    /** Actually draw a sphere. */
    private void actuallyDrawSphere(double x, double y, double z, double r,
       int rgb, int transp){
 if(analyticalSpheres || transp != 255){
     drawAccurateSphere(x, y, z, r, rgb, transp);
     return;
 }
 int xs, ys, rs, rs2, rsrs;
 double zs;
 int zback, zfront;
 Matrix m = overallMatrix;
 // need floating point z coordinate to get clipping right
 double overallScale = getOverallScale();
 xs = (int)(x*m.x00 + y*m.x10 + z*m.x20 + m.x30 + 0.5);
 ys = (int)(x*m.x01 + y*m.x11 + z*m.x21 + m.x31 + 0.5);
 zs = x*m.x02 + y*m.x12 + z*m.x22 + m.x32;
 //Log.info("zs %8.3f", zs);
 //zs *= overallScale;
 int zscale = (int)(zs*ZFixedBits);
 //Log.info("zscale %d", zscale);
 double rsd = overallScale * r;
 rs = (int)(overallScale * r);
 zfront = (int)((zs + r) * ZFixedBits);
 if(zfront < backClip){
     return;
 }
 zback = (int)((zs - r) * ZFixedBits);
 if(zback > frontClip){
     return;
 }
 rs2 = rs * rs;
 rsrs = 2 * rs;
 int ymin = -rs, ymax = rs;
 int xmin = -rs, xmax = rs;
 int zb[] = zbuffer;
 int pb[] = pbuffer;
 int sz[] = null;
 int sc[] = null;
 // little effect
 //if(xs < -rs || ys < -rs || ys > (h + rs) || xs > (w + rs)){
 //    return;
 //}
 // look for this size/colour sphere in the sphere cache.
 for(int i = 0; i < cacheCount; i++){
     if(sradius[i] == rs && scolor[i] == rgb){
  sz = szCache[i];
  sc = scCache[i];
  break;
     }
 }
 // wasn't there so make it.
 if(sz == null){
     generateSphereBitmap(r, rsd, rgb);
     sz = sphereZ;
     sc = sphereColor;
 }
 if(ys < rs) ymin = -ys;
 if(xs < rs) xmin = -xs;
 if(pixelHeight - ys < rs) ymax = pixelHeight - ys;
 if(pixelWidth - xs < rs) xmax = pixelWidth - xs;
 boolean back = false;
 if(zscale < frontClip){
     for(int iy = ymin; iy < ymax; iy++){
  int pixel = INDEX(xs + xmin, ys + iy);
  // have to invert y for the bitmap lookup
  int bitmapPixel = (xmin + rs) + (-iy + rs - 1) * rsrs;
  for(int ix = xmin; ix < xmax; ix++){
      if(sz[bitmapPixel] != 0){
   int iz = 0;
   iz = zscale + sz[bitmapPixel];
   if(iz > zb[pixel] && ((iz) >= backClip && (iz) <= frontClip)){
       zb[pixel] = iz;
       if(transp == 255){
    pb[pixel] = sc[bitmapPixel];
       }else{
    pb[pixel] = Color32.blend(sc[bitmapPixel], pb[pixel], transp);
       }
   }else if(iz > frontClip){
       zb[pixel] = frontClip;
       pb[pixel] = (rgb>>2)&0x3F3F3F;
       //pb[pixel] = (rgb);
   }
      }
      pixel++;
      bitmapPixel++;
  }
     }
 }else{
     for(int iy = ymin; iy < ymax; iy++){
  int pixel = INDEX(xs + xmin, ys + iy);
  // have to invert y for the bitmap lookup
  int bitmapPixel = (xmin + rs) + (-iy + rs - 1) * rsrs;
  for(int ix = xmin; ix < xmax; ix++){
      if(sz[bitmapPixel] != 0){
   int iz = 0;
   iz = zscale - sz[bitmapPixel];
   if(iz < frontClip){
       zb[pixel] = frontClip;
       pb[pixel] = (rgb>>2)&0x3F3F3F;
       //pb[pixel] = (rgb);
   }
      }
      pixel++;
      bitmapPixel++;
  }
     }
 }
    }
    private DoubleArray stransx = new DoubleArray();
    private DoubleArray stransy = new DoubleArray();
    private DoubleArray stransz = new DoubleArray();
    private DoubleArray stransxt = new DoubleArray();
    private DoubleArray stransyt = new DoubleArray();
    private DoubleArray stranszt = new DoubleArray();
    private DoubleArray stransr = new DoubleArray();
    private DoubleArray stransrt = new DoubleArray();
    private IntArray stransrgb = new IntArray();
    private IntArray stransp = new IntArray();
    private IntArray stransid = new IntArray();
    /** Add a transparent sphere to the cache. */
    private void cacheTransparentSphere(double x, double y, double z, double r,
     int rgb, int transp){
 int id = stransx.size();
 stransid.add(id);
 stransx.add(x);
 stransy.add(y);
 stransz.add(z);
 stransr.add(r);
 stransrgb.add(rgb);
 stransp.add(transp);
 applyTransform(x, y, z, cx1);
 cx1[2] *= getOverallScale();
 stransxt.add(cx1[0]);
 stransyt.add(cx1[1]);
 stranszt.add(cx1[2]);
 stransrt.add(r * getOverallScale());
    }
    /** Is this point inside another transparent sphere. */
    private boolean spherePointVisible(double x, double y, double z){
 double stx[] = stransxt.getArray();
 double sty[] = stransyt.getArray();
 double stz[] = stranszt.getArray();
 double str[] = stransrt.getArray();
 int overlapCount = sphereOverlaps.size();
 for(int ii = 0; ii < overlapCount; ii++){
     int i = sphereOverlaps.get(ii);
     double dx = x - stx[i];
     double dy = y - sty[i];
     double dz = z - stz[i];
     double rt = str[i];
     if(dx*dx + dy*dy + dz*dz < rt*rt){
  return false;
     }
 }
 return true;
    }
    /** Draw accurate sphere. */
    protected void drawAccurateSphere(double x, double y, double z,
                                      double r, int rgb, int transp){
 double overallScale = getOverallScale();
 double overallScale1 = 1./getOverallScale();
 double rt = overallScale * r;
 boolean top = true;
 int hit = 0;
 int miss = 0;
 applyTransform(x, y, z, cx1);
 if(cx1[2] - rt > front) return;
 if(cx1[2] + rt < back) return;
 if(background == Color32.black){
     rgb = depthCueShadedColor(rgb, (int)(cx1[2]*ZFixedBits));
 }
 cx1[2] *= overallScale;
 double tx = cx1[0];
 double ty = cx1[1];
 double tz = cx1[2];
        int ambientOcclusion = 128;
 // store the transformed coordinates as
 // we will be shadowing intersection points
 // from the transformed spheres
 if(shadowMode == ShadowsAccumulate){
     ShadowCache.addSphereToCacheList(tx, ty, tz, rt);
     return;
 }else if(shadowMode == ShadowsOn){
     ShadowCache.prepareSphereCacheList(tx, ty, tz, rt, false);
            if(false){
                int occluded = 0;
                double rt22 = 4.0 * rt;
                rt22 *= rt22;
                int ns = ShadowCache.scachex.size();
                for(int s = 0; s < ns; s++){
                    double dx = tx - ShadowCache.scachex.get(s);
                    double dy = ty - ShadowCache.scachey.get(s);
                    double dz = tz - ShadowCache.scachez.get(s);
                    double d2 = dx*dx + dy*dy + dz*dz;
                    if(d2 < rt22){
                        occluded++;
                    }
                }
                //System.out.println("occluded " + occluded);
                int maxOcclusion = 80;
                int minOcclusion = 20;
                if(occluded > maxOcclusion) occluded = maxOcclusion;
                if(occluded < minOcclusion) occluded = minOcclusion;
                ambientOcclusion = (127-64) +
                    (int)((128 + 64)*
                          ((double)(maxOcclusion - occluded)/(maxOcclusion - minOcclusion)));
                if(ambientOcclusion > 255) ambientOcclusion = 255;
                rgb = Color32.scale(rgb, ambientOcclusion);
                //System.out.println("ambientOcclusion " + ambientOcclusion);
            }
 }
 int pxmin = (int)(tx - rt - 3);
 int pxmax = (int)(tx + rt + 3);
 int pymin = (int)(ty - rt - 3);
 int pymax = (int)(ty + rt + 3);
 if(pxmin > pixelWidth || pymin > pixelHeight || pxmax < 0 || pymax < 0){
     return;
 }
 if(pxmin < 0) pxmin = 0;
 if(pymin < 0) pymin = 0;
 if(pxmax > pixelWidth) pxmax = pixelWidth;
 if(pymax > pixelHeight) pymax = pixelHeight;
 int pb[] = pbuffer;
 int zb[] = zbuffer;
 double r2 = rt * rt;
 double r1 = 1./rt;
 int clipColor = (rgb>>2)&0x3F3F3F;
 int cm[] = colorMap;
 for(int j = pymin; j < pymax; j++){
     int px = INDEX(pxmin, j);
     double dy = j - ty;
     double dy2 = dy*dy;
     double sn1 = dy;
     sn1 *= r1;
     int iny = (int)(NormalSamples - sn1 * NormalSamples);
     iny <<= NormalBits;
     int lastIntCount = 0;
     for(int i = pxmin; i < pxmax; i++){
  double dx = i - tx;
  double d2 = dy2 + dx*dx;
  if(d2 < r2){
      // we have intersection...
      //double h = Math.sqrt(r2 - d2);
      double h = fastSqrt(r2 - d2);
      // iterate through the solutions
      //for(int sol = +1; sol >= -1; sol -= 2){
      for(int sol = +1; sol >= 0; sol -= 2){
   double zpos = tz + sol * h;
   double zp = zpos;
   zpos *= overallScale1;
   int izpos = (int)(zpos *ZFixedBits);
   if(izpos > zb[px] && izpos > backClip &&
      izpos < frontClip){
       if(true || shadowMode == ShadowsOff ||
          ShadowCache.pointInSphere(i, j, zp) == false){
    double sn0 = dx * r1;
    int inx = (int)(NormalSamples + sn0 * NormalSamples);
    int lutID = inx + iny;
    if(sol == -1){
        // temp values for the inside solution
        // need to invert the normals, to make lighting
        // look a bit better
        int tinx = (int)(NormalSamples - sn0 * NormalSamples);
        int tiny = (int)(NormalSamples + sn1 * NormalSamples);
        tiny <<= NormalBits;
        lutID = tinx + tiny;
    }
    int c = Color32.multiply(rgb, diffuseMap[lutID]);
    // XXX
    //int s = highlightMap[lutID];
    //c = Color32.add(c, s);
    if(shadowMode == ShadowsOn){
       if(sol == -1 ||
          ShadowCache.pointShadowed(i, j, zp) == true){
           c = Color32.multiply(rgb, shadowMap[lutID]);
       }else{
           int s = highlightMap[lutID];
           c = Color32.add(c, s);
       }
    }else{
        int s = highlightMap[lutID];
        c = Color32.add(c, s);
    }
    if(background != Color32.black){
        c = depthCueShadedColor(c, izpos);
    }
    if(transp != 255){
        if(spherePointVisible(i, j, zp)){
     c = Color32.blend(c, pb[px], transp);
     pb[px] = c;
     zb[px] = izpos;
        }
    }else{
        pb[px] = c;
        zb[px] = izpos;
    }
    break;
       }
       /*}else if(izpos > frontClip){
			      pb[px] = clipColor;
			      zb[px] = frontClip;
			    */
   }
      }
      //hit++;
  }else{
      //miss++;
      // if we are past halfway and we are
      // outside the sphere we can bail out
      // should calculate span line extent properly
      if(dx > 0){
   break;
      }
  }
  px++;
     }
 }
 //System.out.println("hit="+hit + " miss="+miss);
    }
    /** Draw a box in transformed screen coords. */
    public void drawBox(int xt, int yt, int zt, int width, int c){
 if(antialias){
     width *= 2;
 }
 if(((zt) >= backClip && (zt) <= frontClip)){
     int halfWidth = 1 + width/2;
     int shade = depthCueColor(c, zt);
     xt >>= FixedBits;
     yt >>= FixedBits;
     for(int i = -halfWidth; i < halfWidth + 1; i++){
  for(int j = -halfWidth; j < halfWidth + 1; j++){
      setPixel(xt + i, yt + j, zt, shade);
  }
     }
        }
    }
    /**
     * Draw an image at the specified location.
     */
    public void drawPixels(double x, double y, double z,
                           int w, int h, int pix[],
                           int hints){
        applyTransform(x, y, z, tix);
        int p = 0;
        for(int j = 0; j < h; j++){
            for(int i = 0; i < w; i++){
                int pixel = pix[p++];
                int xs = (int)tix[0] + i;
                int ys = (int)tix[1] + j - h;
                if(xs >= 0 && ys >= 0 && xs < pixelWidth && ys < pixelHeight){
                    int zs = (int)(tix[2] * ZFixedBits);
                    int alpha = (pixel >> 24)& 0xff;
                    if(alpha > 0){
                        int pixelIndex = xs + ys * pixelWidth;
                        if(antialias){
                            int bg = opbuffer[pixelIndex];
                            int newp = Color32.blend(pixel, bg, alpha);
                            opbuffer[pixelIndex] = newp;
                        }else{
                            int bg = pbuffer[pixelIndex];
                            int newp = Color32.blend(pixel, bg, alpha);
                            setPixel(xs, ys, zs, newp);
                        }
                    }
                }
            }
        }
    }
    /** Draw a string at the specified point. */
    public void drawDirectString(int x, int y, int color, String string){
 int stringLength = string.length();
 if(antialias){
     // if we are antialiasing we
     // must force to lie on even pixel
     x &= 0xfffffffe;
     y &= 0xfffffffe;
 }
 setupString(string, charOffsets);
 byte font[] = null;
 for(int i = 0; i < stringLength; i++){
     char c = string.charAt(i);
     if(c == '\\'){
  i++;
  c = string.charAt(i);
  font = greekBitmapFont;
     }else{
  font = romanBitmapFont;
     }
     x += drawChar(x + (int)(charOffsets[0] + 0.5),
     y - (int)(charOffsets[1] + 0.5), 0,
     c, font, color, true, null, null, false);
     //System.out.println("x " + x);
 }
    }
    /** Offsets for the character positioning. */
    private double charOffsets[] = new double[3];
    /** Justification styles for text. */
    private static final int JustifyLeft = 1;
    private static final int JustifyRight = 2;
    private static final int JustifyTop = 4;
    private static final int JustifyBottom = 8;
    private static final int JustifyVertical = 16;
    private static final int JustifyHorizontal = 32;
    private static final int JustifyDefault = JustifyLeft | JustifyBottom;
    /** Bounding box of drawn string. */
    private double fontMin[] = new double[3];
    private double fontMax[] = new double[3];
    /** Attributes that are set in the string prefix. */
    private int stringJustification = JustifyDefault;
    private int stringColor = Color32.white;
    private boolean colorDefined = false;
    private double stringSize = 0.5;
    private double stringRadius = -1.0;
    private boolean string3d = false;
    /** Transformed coordinates for the text. */
    private double tix[] = new double[3];
    /** The reference to the roman bitmap font. */
    private byte romanBitmapFont[] = null;
    /** The reference to the greek bitmap font. */
    private byte greekBitmapFont[] = null;
    /**
     * Draw a string at the specified point.
     * All strings are drawn using world coordinates.
     */
    public void drawString(double x, double y, double z, int color, String string){
 drawString(x, y, z, 0.0, color, string);
    }
    /**
     * Draw a string at the specified point.
     * All strings are drawn using world coordinates.
     * zoff controls screen space offseting so that you can
     * force a label to lie in front of other objects.
     */
    public void drawString(double x, double y, double z, double zoff,
      int color, String string){
 setupString(string, charOffsets);
 if(colorDefined == false){
     stringColor = color;
 }
 if(string3d == true){
     drawHersheyString(x, y, z, zoff, 0.5, color, string);
 }else{
     applyTransform(x, y, z, tix);
     drawBitmapString((int)tix[0], (int)tix[1], (int)(tix[2]*ZFixedBits),
        zoff, color, string);
 }
    }
    /** Pixel boundary of current string. */
    private int pixMin[] = new int[3];
    private int pixMax[] = new int[3];
    /** Draw a string at the specified point. */
    private void drawBitmapString(int x, int y, int z, double zoff,
      int color, String string){
 // apply the z-offset
 z += (int)(zoff * ZFixedBits);
 if(((z) >= backClip && (z) <= frontClip)){
     int shade = depthCueColor(stringColor, z);
     int stringLength = string.length();
     // skip any lead in format
     int firstChar = 0;
     if(string.charAt(0) == '<'){
  // if it isn't there it will be set to zero
  // because of the +1
  firstChar = string.indexOf('>') + 1;
     }
     byte font[] = null;
     for(int i = 0; i < 3; i++){
  pixMin[i] = Integer.MAX_VALUE;
  pixMax[i] = Integer.MIN_VALUE;
     }
     for(int pass = 0; pass < 2; pass++){
  boolean measure = (pass == 0);
  int xstart = x;
  int ystart = y;
  if(pass == 1){
      if((stringJustification & JustifyLeft) != 0){
   xstart = x;
      }else if((stringJustification & JustifyRight) != 0){
   xstart = x - (pixMax[0] - pixMin[0]);
      }else if((stringJustification & JustifyHorizontal) != 0){
   xstart = x - (pixMax[0] - pixMin[0])/2;
      }
      if((stringJustification & JustifyBottom) != 0){
   ystart = y;
      }else if((stringJustification & JustifyTop) != 0){
   ystart = y + (pixMax[1] - pixMin[1]);
      }else if((stringJustification & JustifyVertical) != 0){
   ystart = y + (pixMax[1] - pixMin[1])/2;
      }
  }
  for(int i = firstChar; i < stringLength; i++){
      char c = string.charAt(i);
      if(c == '\\'){
   i++;
   c = string.charAt(i);
   font = greekBitmapFont;
      }else{
   font = romanBitmapFont;
      }
      int xpos = xstart + (int)(charOffsets[0] + 0.5);
      int ypos = ystart - (int)(charOffsets[1] + 0.5);
      // force to lie on 0'th pixel of sample
      xpos = samples * (xpos/samples);
      ypos = samples * (ypos/samples);
      xstart += drawChar(xpos, ypos, z,
           c, font, shade, false,
           pixMin, pixMax, measure);
  }
     }
 }
    }
    /** Draw a character at the specified point. */
    private int drawChar(int xOrigin, int yOrigin, int zOrigin,
    char c,
    byte bitmapFont[], int color,
    boolean overlay,
    int min[], int max[], boolean measure){
 if(bitmapFont == null){
     Log.error("null font");
     return 0;
 }
 if(c < 32 || c > 126){
     Log.warn("character not in printable range: %d", (int)c );
     c = '*';
 }
 int maxCharWidth = bitmapFont[0];
 int maxCharHeight = bitmapFont[1];
 int maxAscent = bitmapFont[2];
 int maxDescent = bitmapFont[3];
 int standardLeading = bitmapFont[4];
 int charIndex = c - 32;
 int gridX = charIndex % 12;
 int gridY = charIndex / 12;
 int bitmapWidth = bitmapFont[charIndex+512];
 //Log.info("maxCharWidth %d", maxCharWidth);
 //Log.info("bitmapWidth %d", bitmapWidth);
 //Log.info("maxAscent %d", maxAscent);
 //Log.info("maxDescent %d", maxDescent);
 int xBitmapOrigin = (gridX) * maxCharWidth;
 int yBitmapOrigin = 9 + (gridY + 1) * maxCharHeight;
 yBitmapOrigin -= maxAscent;
 yOrigin -= maxAscent;
 // another offset as letter is twice as big
 yOrigin -= (samples - 1) * maxAscent;
 int bitmapHeight = maxAscent + maxDescent;
 //System.out.println("char "  +c+ " xBitmapOrigin " + xBitmapOrigin +
 //		   " yBitmapOrigin " + yBitmapOrigin + " width " + bitmapWidth);
        for(int y = 0; y < bitmapHeight; y++){
            int yPixel = yBitmapOrigin + y;
            for(int x = 0; x < bitmapWidth; x++){
  int xPixel = xBitmapOrigin + x;
  int bitmapPixel = xPixel + yPixel * 512;
  //System.out.print(bitmapFont[bitmapPixel]);
                if(bitmapFont[bitmapPixel] != 0){
      if(measure){
   for(int ay = 0; ay < samples; ay++){
       for(int ax = 0; ax < samples; ax++){
    int xp = xOrigin + samples * x + ax;
    int yp = yOrigin + samples * y + ay;
    if(xp < min[0]) min[0] = xp;
    if(yp < min[1]) min[1] = yp;
    if(xp > max[0]) max[0] = xp;
    if(yp > max[1]) max[1] = yp;
       }
   }
      }else{
   for(int ay = 0; ay < samples; ay++){
       for(int ax = 0; ax < samples; ax++){
    if(overlay){
        setPixel(xOrigin + samples * x + ax,
          yOrigin + samples * y + ay,
          color);
    }else{
        setPixel(xOrigin + samples * x + ax,
          yOrigin + samples * y + ay,
          zOrigin,
          color);
    }
       }
   }
      }
      if(false){
   if(overlay){
       if(samples > 1){
    for(int ay = 0; ay < samples; ay++){
        for(int ax = 0; ax < samples; ax++){
     setPixel(xOrigin + samples * x + ax,
       yOrigin + samples * y + ay, color);
        }
       }
       }else{
    setPixel(xOrigin + x, yOrigin + y, color);
       }
   }else{
       if(samples > 1){
    for(int ay = 0; ay < samples; ay++){
        for(int ax = 0; ax < samples; ax++){
     setPixel(xOrigin + samples * x + ax,
       yOrigin + samples * y + ay, zOrigin,
       color);
        }
    }
       }else{
    setPixel(xOrigin + x, yOrigin + y, zOrigin, color);
       }
   }
      }
                }
            }
     //System.out.println("");
        }
 return samples * (bitmapWidth + standardLeading);
 //if(antialias){
     //return 2 * (bitmapWidth + standardLeading);
 //}else{
 //return bitmapWidth + standardLeading;
 //}
    }
    /** Some scaling values for mapping hershey to Angstroms. */
    private double hersheyScale = -1.0;
    private double hersheyRadius = -1.0;
    private double fontHeight = 0.0;
    private double fontRadius = 0.0;
    /** On screen transformed coordinates system. */
    private Point3d xd = new Point3d();
    private Point3d yd = new Point3d();
    private Point3d zd = new Point3d();
    /** Draw a string using a hershey font. */
    private void drawHersheyString(double x, double y, double z,
       double zoff,
       double size,
       int color, String string){
 //stringJustification = JustifyDefault;
 //stringColor = color;
        // Mike, can you say matrix?
 // construct on screen orthogonal vector set
 xd.set(1., 0., 0.);
 yd.set(0., -1., 0.);
 zd.set(0., 0., 1.);
 // non-negative size will override the size
 // from the string setup
 // calculate height for this string
 fontHeight = stringSize * hersheyScale;
 fontRadius = stringSize * stringRadius;
 double xorig = x;
 double yorig = y;
 double zorig = z;
 for(int pass = 0; pass < 2; pass++){
     x = xorig;
     y = yorig;
     z = zorig;
     if(pass == 0){
  for(int i = 0; i < 3; i++){
      fontMin[i] = 1.e10;
      fontMax[i] = -1.e10;
  }
     }else{
  // transform the axes now
  rotationMatrix.transformByInverse(xd);
  rotationMatrix.transformByInverse(yd);
  rotationMatrix.transformByInverse(zd);
     }
     if(pass == 1){
  double dx = 0.0;
  double dy = 0.0;
  /*
		for(int i = 0; i < 3; i++){
		    FILE.out.print("%d ", i);
		    FILE.out.print("%f ", fontMin[i]);
		    FILE.out.print("%f\n", fontMax[i]);
		}
		*/
  if((stringJustification & JustifyLeft) != 0){
      dx = x - fontMin[0];
  }else if((stringJustification & JustifyRight) != 0){
      dx = x - fontMax[0];
  }else if((stringJustification & JustifyHorizontal) != 0){
      dx = x - 0.5 * (fontMax[0] + fontMin[0]);
  }
  if((stringJustification & JustifyBottom) != 0){
      dy = y - fontMin[1];
  }else if((stringJustification & JustifyTop) != 0){
      dy = y - fontMax[1];
  }else if((stringJustification & JustifyVertical) != 0){
      dy = y - 0.5 * (fontMax[1] + fontMin[1]);
  }
  x += dx * xd.x;
  y += dx * xd.y;
  z += dx * xd.z;
  // y goes negative
  x -= dy * yd.x;
  y -= dy * yd.y;
  z -= dy * yd.z;
  x += charOffsets[0] * xd.x;
  y += charOffsets[0] * xd.y;
  z += charOffsets[0] * xd.z;
  // y goes negative
  x -= charOffsets[1] * yd.x;
  y -= charOffsets[1] * yd.y;
  z -= charOffsets[1] * yd.z;
  x += charOffsets[2] * zd.x;
  y += charOffsets[2] * zd.y;
  z += charOffsets[2] * zd.z;
     }
     try {
  DynamicArray hersheyFont = getHersheyFont("hershey.normal");
  int shade = stringColor;
  int stringLength = string.length();
  // skip any lead in format
  int firstChar = 0;
  if(string.charAt(0) == '<'){
      // if it isn't there it will be set to zero
      // because of the +1
      firstChar = string.indexOf('>') + 1;
  }
  for(int i = firstChar; i < stringLength; i++){
      char c = string.charAt(i);
      if(c == '\\'){
   i++;
   c = string.charAt(i);
   hersheyFont = getHersheyFont("hershey.greek");
      }else{
   hersheyFont = getHersheyFont("hershey.normal");
      }
      double xshift = drawHersheyChar3d(x, y, z, zoff,
            size, c, hersheyFont, shade,
            pass == 0 ? true : false,
            fontMin, fontMax);
      // shift along on screen direction
      x += xshift * xd.x;
      y += xshift * xd.y;
      z += xshift * xd.z;
  }
     }catch(Exception e){
  System.out.println("exception " + e);
     }
     fontMin[0] -= fontRadius;
     fontMin[1] -= fontRadius;
     fontMax[0] += fontRadius;
     fontMax[1] += fontRadius;
 }
    }
    /** Draw a single hershey font char. */
    private double drawHersheyChar3d(double x, double y, double z,
         double zoff,
         double size,
         char c, DynamicArray font,
         int shade,
         boolean measure,
         double fmin[], double fmax[]){
 String s = (String)font.get(c-32);
 int len = s.length();
 //System.out.println("data " + s);
 // margins of this glyph
 double lm = ((s.charAt(0)-'R')) * fontHeight;
 double rm = ((s.charAt(1)-'R')) * fontHeight;
 x -= lm * xd.x;
 y -= lm * xd.y;
 z -= lm * xd.z;
 int lastx = -1;
 int lasty = -1;
 int nextx = -1;
 int nexty = -1;
 for(int i = 2; i < len; i += 2){
     char c0 = s.charAt(i);
     char c1 = s.charAt(i+1);
     if(c0 == ' ' && c1 == 'R'){
  nextx = -1;
  nexty = -1;
     }else{
  nextx = (c0 - 'R');
  // the -9 reflects the origin of the hershey
  // coordinate system.
  //		nexty = (c1 - 'R') - 9;
  nexty = (c1 - 'R');
  if(lastx != -1 || lasty != -1){
      // XXX
      // why aren't these arrays?
      double xnew = nextx * xd.x + nexty * yd.x;
      double ynew = nextx * xd.y + nexty * yd.y;
      double znew = nextx * xd.z + nexty * yd.z;
      double xold = lastx * xd.x + lasty * yd.x;
      double yold = lastx * xd.y + lasty * yd.y;
      double zold = lastx * xd.z + lasty * yd.z;
      double xstart = x + xnew * fontHeight + zoff * zd.x;
      double ystart = y + ynew * fontHeight + zoff * zd.y;
      double zstart = z + znew * fontHeight + zoff * zd.z;
      double xstop = x + xold * fontHeight + zoff * zd.x;
      double ystop = y + yold * fontHeight + zoff * zd.y;
      double zstop = z + zold * fontHeight + zoff * zd.z;
      if(measure){
   if(xstart > fmax[0]) fmax[0] = xstart;
   if(ystart > fmax[1]) fmax[1] = ystart;
   if(xstop > fmax[0]) fmax[0] = xstop;
   if(ystop > fmax[1]) fmax[1] = ystop;
   if(xstart < fmin[0]) fmin[0] = xstart;
   if(ystart < fmin[1]) fmin[1] = ystart;
   if(xstop < fmin[0]) fmin[0] = xstop;
   if(ystop < fmin[1]) fmin[1] = ystop;
      }else{
   drawCylinder(xstart, ystart, zstart,
         xstop, ystop, zstop,
         shade, shade, fontRadius);
      }
  }
     }
     lastx = nextx;
     lasty = nexty;
 }
 return rm - lm;
    }
    /** Where the character data is stored. */
    private Hashtable hersheyHash = new Hashtable();
    /** Make sure we got the hershey fonts loaded. */
    private DynamicArray getHersheyFont(String name){
 DynamicArray hersheyFont = (DynamicArray)hersheyHash.get(name);
 if(hersheyFont == null){
     String hersheyFontName = Settings.getString("fonts", name);
     FILE hf = FILE.open(hersheyFontName);
     if(hf == null){
  Log.error("couldn't open " + hersheyFontName);
  return null;
     }
     hersheyFont = new DynamicArray();
     while(hf.nextLine()){
  String line = hf.getCurrentLineAsString();
  //System.out.println("line " + line);
  //hersheyFont.add(line);
  hersheyFont.add(line.substring(8));
     }
     hf.close();
     hersheyHash.put(name, hersheyFont);
 }
 return hersheyFont;
    }
    /** Default string point size for bitmaps. */
    private final int defaultBitmapFontSize =
 Settings.getInteger("fonts", "defaultbitmapfontsize");
    /** Set up the font and offsets for a string rendering. */
    private void setupString(String s, double charOffsets[]){
 String fontname = null;
 charOffsets[0] = 0.0;
 charOffsets[1] = 0.0;
 charOffsets[2] = 0.0;
 stringJustification = JustifyDefault;
 stringColor = Color32.white;
 stringSize = 0.5;
 stringRadius = hersheyRadius;
 string3d = false;
 colorDefined = false;
 int stringPoints = defaultBitmapFontSize;
 if(s.startsWith("<")){
     int pos = s.indexOf('>');
     if(pos == -1){
  Log.error("unterminated string intro: no matching >");
     }else{
  String format = s.substring(1, pos);
  String tokens[] = FILE.split(format, ",");
  //System.out.println("format |" + format + "|");
  if(format.indexOf('=') != -1){
      for(int i = 0; i < tokens.length; i++){
   if(tokens[i].indexOf('=') != -1){
       String bits[] = FILE.split(tokens[i], "=");
       String option = bits[0];
       if(option.equals("justify")){
    stringJustification = 0;
    if(bits[1].indexOf('l') != -1){
        stringJustification |= JustifyLeft;
    }
    if(bits[1].indexOf('r') != -1){
        stringJustification |= JustifyRight;
    }
    if(bits[1].indexOf('t') != -1){
        stringJustification |= JustifyTop;
    }
    if(bits[1].indexOf('b') != -1){
        stringJustification |= JustifyBottom;
    }
    if(bits[1].indexOf('v') != -1){
        stringJustification |= JustifyVertical;
    }
    if(bits[1].indexOf('h') != -1){
        stringJustification |= JustifyHorizontal;
    }
       }else if(option.equals("color") || option.equals("colour")){
    stringColor = Color32.getColorFromName(bits[1]);
    colorDefined = true;
       }else if(option.equals("points")){
    stringPoints = FILE.readInteger(bits[1]);
       }else if(option.equals("size")){
    stringSize = FILE.readDouble(bits[1]);
       }else if(option.equals("radius")){
    stringRadius = FILE.readDouble(bits[1]);
       }else if(option.equals("xoff")){
    charOffsets[0] = FILE.readDouble(bits[1]);
       }else if(option.equals("yoff")){
    charOffsets[1] = FILE.readDouble(bits[1]);
       }else if(option.equals("zoff")){
    charOffsets[2] = FILE.readDouble(bits[1]);
       }else if(option.equals("font")){
    fontname = bits[1];
       }else if(option.equals("3d")){
    string3d = bits[1].startsWith("t");
       }else{
    Log.error("unrecognized font keyword " + tokens[i]);
       }
   }
      }
  }else{
      if(tokens.length == 1){
   // just the font name.
   fontname = tokens[0];
      }else if(tokens.length == 2){
   charOffsets[0] = FILE.readDouble(tokens[0]);
   charOffsets[1] = FILE.readDouble(tokens[1]);
      }else if(tokens.length == 3){
   charOffsets[0] = FILE.readDouble(tokens[0]);
   charOffsets[1] = FILE.readDouble(tokens[1]);
   fontname = tokens[2];
      }else{
   //Log.error("must be 1, 2 or 3 comma separated tokens in format");
      }
  }
     }
 }
 if(string3d == false){
     // we need to setup bitmap fonts.
     romanBitmapFont = getFont("arial." + stringPoints);
     greekBitmapFont = getFont("symbol." + stringPoints);
 }
    }
    /** Return the default font. */
    private byte[] defaultFont(){
 return getFont("default");
    }
    /** Hash of the bitmap fonts that we have loaded. */
    private Hashtable bitmapFonts = new Hashtable();
    /** Look up a font from the bitmapfont properties. */
    private byte[] getFont(String name){
 // special case for default font
 if(name.equals("default")){
     name = Settings.getString("fonts", "default");
 }
 byte font[] = (byte [])bitmapFonts.get(name);
 if(font == null){
     font = new byte[512*512];
     String fontFile = Settings.getString("fonts", name);
     if(fontFile == null){
  Log.error("no such font as " + name);
  return defaultFont();
     }
     FILE file = FILE.open(fontFile);
     if(file == null){
  Log.error("couldn't find font " + name);
  Log.error("was looking for    " + fontFile);
  return defaultFont();
     }
     InputStream input = file.getInputStream();
     try {
  GZIPInputStream gis = new GZIPInputStream(input);
  // convert to datainputstream to get
  // the readfully method...
  DataInputStream dis = new DataInputStream(gis);
  dis.readFully(font);
  dis.close();
  bitmapFonts.put(name, font);
     }catch(Exception e){
  System.out.println("failed to open gzip'ed font");
  System.out.println("" + e);
     }
 }
 return font;
    }
    public void drawZImage(double x, double y, double z,
                           int pix[], int w, int h){
    }
    /** Draw a dot. */
    public void drawDot(int xt, int yt, int zt, int c){
 if(((zt) >= backClip && (zt) <= frontClip)){
     int shade = depthCueColor(c, zt);
     xt >>= FixedBits;
     yt >>= FixedBits;
     setPixel(xt, yt, zt, shade);
     setPixel(xt+1, yt, zt, shade);
     setPixel(xt, yt+1, zt, shade);
     setPixel(xt+1, yt+1, zt, shade);
 }
    }
    /** Set a pixel checking for on screen and z-buffer. */
    public void setPixel(int x, int y, int z, int c){
 if(x >= 0 && x < pixelWidth && y >= 0 && y < pixelHeight){
     int pos = INDEX(x, y);
     if(zbuffer[pos] < z){
  pbuffer[pos] = c;
  zbuffer[pos] = z;
     }
 }
    }
    /** Set a pixel checking for on screen. */
    public void setPixel(int x, int y, int c){
 if(x >= 0 && x < pixelWidth && y >= 0 && y < pixelHeight){
     int pos = INDEX(x, y);
     pbuffer[pos] = c;
 }
    }
    /** Get a pixel value checking for on screen. */
    public int getPixel(int x, int y){
 if(x >= 0 && x < pixelWidth && y >= 0 && y < pixelHeight){
     int pos = INDEX(x, y);
     return pbuffer[pos];
 }
        return 0;
    }
    /*
     * Draw lines in the renderer.
     */
    private double lineRadius = -1.0;
    // activate the code that does antialiased line drawing...
    //#define AA
    /**
     * Draw a line from the current tmesh object.
     */
    private void drawLine(Tmesh tmesh, int v1, int v2, int rgb1, int rgb2, int pixelWidth){
 int outCode1 = clipped[v1];
 int outCode2 = clipped[v2];
 int rgb1shade = 0, rgb2shade = 0;
 if(false && shadowMode != ShadowsOff){
     drawCylinder(tmesh.x[v1], tmesh.y[v1], tmesh.z[v1],
    tmesh.x[v2], tmesh.y[v2], tmesh.z[v2],
    rgb1, rgb2, (double)(pixelWidth * 0.02));
     return;
 }
 if(antialias){
     pixelWidth *= 2;
 }
 if(pixelWidth > 3){
     pixelWidth = 3;
 }
 int x1 = xt[v1] >> FixedBits;
 int y1 = yt[v1] >> FixedBits;
 int z1 = zt[v1];
 int x2 = xt[v2] >> FixedBits;
 int y2 = yt[v2] >> FixedBits;
 int z2 = zt[v2];
 int zc = (z1 + z2)/2;
 int scale = 255;
 if(!depthcue){
     if(zc > frontClip){
  scale = 255;
     }else if(zc < backClip){
  scale = 0;
     }else{
  scale = (int)(255 * (double)(zc - backClip)/
         (frontClip - backClip));
     }
     rgb1shade = depthCueColor(rgb1, zc);
     if(rgb1 != rgb2){
  rgb2shade = depthCueColor(rgb2, zc);
     }
     //rgb1shade = Color32.scale(rgb1, scale);
     //if(rgb1 != rgb2){
     //	rgb2shade = Color32.scale(rgb2, scale);
     //}
 }else{
     // no fog so don't shade at all
     // this will be dealt with by image
     // post processing.
     rgb1shade = rgb1;
     rgb2shade = rgb2;
 }
 if(outCode1 == 0 && outCode2 == 0){
     // completely on screen
     if(rgb1 == rgb2){
  if(pixelWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb1shade);
      }else{
   drawFastIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade, pixelWidth);
  }
     }else{
  int xc = (x1 + x2)/2;
  int yc = (y1 + y2)/2;
  if(pixelWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, xc, yc, zc, rgb1shade, rgb1shade);
   drawAntiAliasedLine(xc, yc, zc, x2, y2, z2, rgb2shade, rgb2shade);
      }else{
   drawFastIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade);
   drawFastIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade, pixelWidth);
      drawWideIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade, pixelWidth);
  }
     }
 }else if((outCode1 & outCode2) == 0){
     // line does not have both points off same side
     if(rgb1 == rgb2){
  if(pixelWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb1shade);
      }else{
   drawSafeIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade, pixelWidth);
  }
     }else{
  int xc = (x1 + x2)/2;
  int yc = (y1 + y2)/2;
  if(pixelWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, xc, yc, zc, rgb1shade, rgb1shade);
   drawAntiAliasedLine(xc, yc, zc, x2, y2, z2, rgb2shade, rgb2shade);
      }else{
   drawSafeIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade);
   drawSafeIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade, pixelWidth);
      drawWideIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade, pixelWidth);
  }
     }
 }
    }
    private double vx1[] = new double[3];
    private double vx2[] = new double[3];
    /** World coordinate entry point for drawing lines. */
    public void drawLine(double x1, double y1, double z1,
    double x2, double y2, double z2,
    int rgb1, int rgb2, int pixelWidth){
 applyTransform(x1, y1, z1, vx1);
 applyTransform(x2, y2, z2, vx2);
 drawLine((int)vx1[0] << FixedBits,
   (int)vx1[1] << FixedBits,
   (int)(vx1[2] * (1<< (FixedBits+8))),
   (int)vx2[0] << FixedBits,
   (int)vx2[1] << FixedBits,
   (int)(vx2[2] * (1<< (FixedBits+8))),
   rgb1, rgb2, pixelWidth);
    }
    /**
     * Main entry point for drawing lines.
     *
     * This method will detect if the line is entirely in the
     * pixel map and draw it fast or safe accordingly.
     */
    public void drawLine(int x1, int y1, int z1,
    int x2, int y2, int z2,
    int rgb1, int rgb2, int lineWidth){
 // generate the sutherland-cohen outcodes.
 int outCode1 = 0;
 int outCode2 = 0;
 int rgb1shade = 0, rgb2shade = 0;
 if(antialias){
     lineWidth *= 2;
 }
 if(lineWidth > 3){
     lineWidth = 3;
 }
 // move back to pixel coords
 x1 >>= FixedBits;
 x2 >>= FixedBits;
 y1 >>= FixedBits;
 y2 >>= FixedBits;
 if(x1 < 0) outCode1 |= XMinClip;
 else if(x1 >= pixelWidth) outCode1 |= XMaxClip;
 if(y1 < 0) outCode1 |= YMinClip;
 else if(y1 >= pixelHeight) outCode1 |= YMaxClip;
 if(z1 < backClip) outCode1 |= ZMinClip;
 else if(z1 > frontClip) outCode1 |= ZMaxClip;
 if(x2 < 0) outCode2 |= XMinClip;
 else if(x2 >= pixelWidth) outCode2 |= XMaxClip;
 if(y2 < 0) outCode2 |= YMinClip;
 else if(y2 >= pixelHeight) outCode2 |= YMaxClip;
 if(z2 < backClip) outCode2 |= ZMinClip;
 else if(z2 > frontClip) outCode2 |= ZMaxClip;
 int zc = (z1 + z2)/2;
 if(!depthcue){
     rgb1shade = depthCueColor(rgb1, z1);
     if(rgb1 != rgb2){
  rgb2shade = depthCueColor(rgb2, z2);
     }
 }else{
     rgb1shade = rgb1;
     rgb2shade = rgb2;
 }
 if(outCode1 == 0 && outCode2 == 0){
     // completely on screen
     if(rgb1 == rgb2){
  if(lineWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb1shade);
      }else{
   drawFastIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade, lineWidth);
  }
     }else{
  int xc = (x1 + x2)/2;
  int yc = (y1 + y2)/2;
  if(lineWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb2shade);
      }else{
   drawFastIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade);
   drawFastIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade, lineWidth);
      drawWideIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade, lineWidth);
  }
     }
 }else if((outCode1 & outCode2) == 0){
     // line does not have both points off same side
     if(rgb1 == rgb2){
  if(lineWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb1shade);
      }else{
   drawSafeIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, x2, y2, z2, rgb1shade, lineWidth);
  }
     }else{
  int xc = (x1 + x2)/2;
  int yc = (y1 + y2)/2;
  if(lineWidth == 1){
      if(wuAntiAlias){
   drawAntiAliasedLine(x1, y1, z1, x2, y2, z2, rgb1shade, rgb2shade);
      }else{
   drawSafeIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade);
   drawSafeIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade);
      }
  }else{
      drawWideIntegerLine(x1, y1, z1, xc, yc, zc, rgb1shade, lineWidth);
      drawWideIntegerLine(xc, yc, zc, x2, y2, z2, rgb2shade, lineWidth);
  }
     }
 }
    }
    private int gamma_table[] = null;
    private double drawGamma = 2.;
    public void setDrawGamma(double d){
        gamma_table = null;
        drawGamma = d;
    }
    protected void initialiseGammaTable(){
        if(gamma_table == null){
            gamma_table = new int[256];
            /* generate gamma correction table */
            for (int i = 0; i < 256; i++){
                gamma_table[i] = (int) ((256-1)*Math.pow((double)i/((double)(256-1)),
                                                         1.0/drawGamma));
            }
        }
    }
    /**
     * Basic Wu antialiased line drawing.
     * Many problems.
     * 1) Linear sharing of intensities
     * doesn't look quite perfect.
     * 2) No z-buffering
     * 3) Imperfect line endings
     */
    public void drawAntiAliasedLine(int x0, int y0, int z0,
                                    int x1, int y1, int z1, int Colour0, int Colour1){
        if(gamma_table == null){
            initialiseGammaTable();
        }
        int dx, dy, dz, xDir, z;
        if(y0>y1) {
            //Swap(y0,y1);
            //Swap(x0,x1);
            int swap = y0;
            y0 = y1;
            y1 = swap;
            swap = x0;
            x0 = x1;
            x1 = swap;
            swap = Colour0;
            Colour0 = Colour1;
            Colour1 = swap;
            swap = z0;
            z0 = z1;
            z1 = swap;
        }
        dx=x1-x0;
        dy=y1-y0;
        if(dx>=0){
            xDir=1;
        }else{
            xDir=-1;
            dx=-dx;
        }
        int Colour = Colour0;
        if(dx==0) { // vertical line
            int ym = (y0 + y1) >> 1;
     dz = dy != 0 ? (z1 - z0)/dy : 0;
     z = z0;
            for(int py = y0 + 1; py <= ym; py++) { blendPixel(x0, py, z, Colour0, 255); z += dz; }
            for(int py = ym + 1; py < y1; py++) { blendPixel(x0, py, z, Colour1, 255); z += dz; }
            return;
        }
        if(dy==0) { // horizontal line
            if(x0>x1) {
                int swap = x0;
                x0 = x1;
                x1 = swap;
                swap = Colour0;
                Colour0 = Colour1;
                Colour1 = swap;
                swap = z0;
                z0 = z1;
                z1 = swap;
  //dx = -dx;
                //Swap(x0,x1);
            }
            int xm = (x0 + x1) >> 1;
     dz = (dx != 0) ? (z1 - z0)/dx : 0;
     z = z0;
            for(int px = x0 + 1; px <= xm; px++) { blendPixel(px, y0, z, Colour0, 255); z += dz; }
     for(int px = xm + 1; px < x1; px++) { blendPixel(px, y0, z, Colour1, 255); z += dz; }
            return;
        }
        int dx2 = dx >> 1;
        int dy2 = dy >> 1;
        blendPixel(x0,y0, z0, Colour0, 255); //First and last Pixels always get Set:
        blendPixel(x1,y1, z1, Colour1, 255);
        // line is not horizontal or vertical: use Wu Antialiasing:
        int ErrorAcc=0;
        int Transparency;
        if(dy>dx) { // y-major line
     dz = dy != 0 ? (z1 - z0)/dy : 0;
     z = z0;
            int ErrorAdj=((int)dx<<16) / (int)dy;
            if(xDir<0) {
                while(--dy != 0) {
                    if(dy <= dy2){
                        Colour = Colour1;
                    }
                    ErrorAcc+=ErrorAdj;
                    ++y0;
                    x1=x0-(int)(ErrorAcc>>16);
                    Transparency=(ErrorAcc>>8);
                    blendPixel(x1 , y0, z, Colour, ~Transparency);
                    blendPixel(x1-1, y0, z, Colour, Transparency);
      z += dz;
                }
            }else{
                while(--dy != 0) {
                    if(dy <= dy2){
                        Colour = Colour1;
                    }
                    ErrorAcc+=ErrorAdj;
                    ++y0;
                    x1=x0+(int)(ErrorAcc>>16);
                    Transparency=(int)(ErrorAcc>>8);
                    blendPixel(x1 , y0, z, Colour, ~Transparency);
                    blendPixel(x1+xDir, y0, z, Colour, Transparency);
      z += dz;
                }
            }
        }else{ // x-major line
     dz = dx != 0 ? (z1 - z0)/dx : 0;
     z = z0;
            int ErrorAdj=((int)dy<<16) / (int)dx;
            while(--dx != 0) {
                if(dx <= dx2){
                    Colour = Colour1;
                }
                ErrorAcc+=ErrorAdj;
                x0+=xDir;
                y1=y0+(int)(ErrorAcc>>16);
                Transparency=(int)(ErrorAcc>>8);
                blendPixel(x0, y1 , z, Colour, ~Transparency);
                blendPixel(x0, y1+1, z, Colour, Transparency);
  z += dz;
            }
        }
    }
    /** Merge pixel color with background and transparency. */
    protected void blendPixel(int x, int y, int z, int c, int transp){
 if(x < 0 || y < 0 || x >= pixelWidth || y >= pixelHeight) return;
        int p = INDEX(x, y);
        if(((z) >= backClip && (z) <= frontClip) && z > zbuffer[p]){
            int bg = pbuffer[p];
            transp &= 0xff;
            transp = gamma_table[transp];
            int fg = Color32.scale(c, transp);
            //setPixel(x, y, Color32.add(bg, ctransp));
            int rbg = (bg >> 16) & 0xff;
            int gbg = (bg >> 8) & 0xff;
            int bbg = (bg & 0xff);
            int rfg = (fg >> 16) & 0xff;
            int gfg = (fg >> 8) & 0xff;
            int bfg = (fg & 0xff);
            c = Color32.pack((rbg > rfg) ? rbg : rfg,
                             (gbg > gfg) ? gbg : gfg,
                             (bbg > bfg) ? bbg : bfg);
            pbuffer[p] = c;
            zbuffer[p] = z;
        }
    }
    protected void blendPixel2(int x, int y, int z, int c, int transp){
        int p = INDEX(x, y);
        if(z > zbuffer[p] && z < frontClip){
            int bg = pbuffer[p];
            transp &= 0xff;
            int fg = Color32.scale(c, transp);
            pbuffer[p] = Color32.add(fg, bg);
        }
    }
    /**
     * Draw a fast integer line.
     *
     * no checks are made for out of bounds as we should only
     * ever be called once we have determined that the line
     * is entirely in the pixel map.
     */
    private void drawFastIntegerLine(int x1, int y1, int z1,
         int x2, int y2, int z2,
         int rgb) {
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
int incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
int z, dz;
int pb[] = pbuffer;
int zb[] = zbuffer;
int dx = x2-x1;
if(dx < 0) dx = -dx;
int dy = y2-y1;
if(dy < 0) dy = -dy;
int pixelIndex = 0;
// single point line
if(dy == 0 && dx == 0){
    x = x1;
    y = y1;
    pixelIndex = y * pixelWidth + x;
    z = z1 > z2 ? z1 : z2;
    if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    return;
}
if (dy <= dx) {
    d = 2*dy - dx;
    incr1 = 2*dy;
    incr2 = 2 * (dy - dx);
    if (x1 > x2) {
 x = x2;
 y = y2;
 z = z2;
 dz = (z1 - z2)/dx;
 ydirflag = (-1);
 xend = x1;
    } else {
 x = x1;
 y = y1;
 z = z1;
 dz = (z2 - z1)/dx;
 ydirflag = 1;
 xend = x2;
    }
    pixelIndex = y * pixelWidth + x;
    if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    if (((y2 - y1) * ydirflag) > 0) {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y++;
  pixelIndex += pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    } else {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y--;
  pixelIndex -= pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    }
} else {
    d = 2*dx - dy;
    incr1 = 2*dx;
    incr2 = 2 * (dx - dy);
    if (y1 > y2) {
 y = y2;
 x = x2;
 z = z2;
 dz = (z1 - z2)/dy;
 yend = y1;
 xdirflag = (-1);
    } else {
 y = y1;
 x = x1;
 z = z1;
 dz = (z2 - z1)/dy;
 yend = y2;
 xdirflag = 1;
    }
    pixelIndex = y * pixelWidth + x;
    if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    if (((x2 - x1) * xdirflag) > 0) {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x++;
  pixelIndex++;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    } else {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x--;
  pixelIndex--;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     if(z > zb[pixelIndex]){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    }
}
    }
    /**
     * Draw a safe integer line.
     *
     * Every pixel is checked for being on the screen
     * Only called when the outcodes indicate the line may be
     * partially visible.
     */
    private void drawSafeIntegerLine(int x1, int y1, int z1,
         int x2, int y2, int z2,
         int rgb) {
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
int incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
int z, dz;
int pb[] = pbuffer;
int zb[] = zbuffer;
int dx = x2-x1;
if(dx < 0) dx = -dx;
int dy = y2-y1;
if(dy < 0) dy = -dy;
int pixelIndex = 0;
// single point line
if(dy == 0 && dx == 0){
    x = x1;
    y = y1;
    pixelIndex = y * pixelWidth + x;
    z = z1 > z2 ? z1 : z2;
    if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    return;
}
if (dy <= dx) {
    d = 2*dy - dx;
    incr1 = 2*dy;
    incr2 = 2 * (dy - dx);
    if (x1 > x2) {
 x = x2;
 y = y2;
 z = z2;
 dz = (z1 - z2)/dx;
 ydirflag = (-1);
 xend = x1;
    } else {
 x = x1;
 y = y1;
 z = z1;
 dz = (z2 - z1)/dx;
 ydirflag = 1;
 xend = x2;
    }
    pixelIndex = y * pixelWidth + x;
    if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    if (((y2 - y1) * ydirflag) > 0) {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y++;
  pixelIndex += pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    } else {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y--;
  pixelIndex -= pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    }
} else {
    d = 2*dx - dy;
    incr1 = 2*dx;
    incr2 = 2 * (dx - dy);
    if (y1 > y2) {
 y = y2;
 x = x2;
 z = z2;
 dz = (z1 - z2)/dy;
 yend = y1;
 xdirflag = (-1);
    } else {
 y = y1;
 x = x1;
 z = z1;
 dz = (z2 - z1)/dy;
 yend = y2;
 xdirflag = 1;
    }
    pixelIndex = y * pixelWidth + x;
    if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
    if (((x2 - x1) * xdirflag) > 0) {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x++;
  pixelIndex++;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    } else {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x--;
  pixelIndex--;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     if(x >= 0 && y >= 0 && x < pixelWidth && y < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;};
 }
    }
}
    }
    /**
     * Draw a safe integer line.
     *
     * Every pixel is checked for being on the screen
     * Only called when the outcodes indicate the line may be
     * partially visible.
     */
    private void drawWideIntegerLine(int x1, int y1, int z1,
         int x2, int y2, int z2,
         int rgb, int width){
/*
 * This file is part of OpenAstexViewer.
 *
 * OpenAstexViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAstexViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OpenAstexViewer.  If not, see <http://www.gnu.org/licenses/>.
 */
int incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
int z, dz;
int pb[] = pbuffer;
int zb[] = zbuffer;
int dx = x2-x1;
if(dx < 0) dx = -dx;
int dy = y2-y1;
if(dy < 0) dy = -dy;
int pixelIndex = 0;
// single point line
if(dy == 0 && dx == 0){
    x = x1;
    y = y1;
    pixelIndex = y * pixelWidth + x;
    z = z1 > z2 ? z1 : z2;
    pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
    return;
}
if (dy <= dx) {
    d = 2*dy - dx;
    incr1 = 2*dy;
    incr2 = 2 * (dy - dx);
    if (x1 > x2) {
 x = x2;
 y = y2;
 z = z2;
 dz = (z1 - z2)/dx;
 ydirflag = (-1);
 xend = x1;
    } else {
 x = x1;
 y = y1;
 z = z1;
 dz = (z2 - z1)/dx;
 ydirflag = 1;
 xend = x2;
    }
    pixelIndex = y * pixelWidth + x;
    pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
    if (((y2 - y1) * ydirflag) > 0) {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y++;
  pixelIndex += pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
 }
    } else {
 while (x < xend) {
     x++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  y--;
  pixelIndex -= pixelWidth;
  d+=incr2;
     }
     pixelIndex++;
     pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
 }
    }
} else {
    d = 2*dx - dy;
    incr1 = 2*dx;
    incr2 = 2 * (dx - dy);
    if (y1 > y2) {
 y = y2;
 x = x2;
 z = z2;
 dz = (z1 - z2)/dy;
 yend = y1;
 xdirflag = (-1);
    } else {
 y = y1;
 x = x1;
 z = z1;
 dz = (z2 - z1)/dy;
 yend = y2;
 xdirflag = 1;
    }
    pixelIndex = y * pixelWidth + x;
    pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
    if (((x2 - x1) * xdirflag) > 0) {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x++;
  pixelIndex++;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
 }
    } else {
 while (y < yend) {
     y++;
     z += dz;
     if (d <0) {
  d+=incr1;
     } else {
  x--;
  pixelIndex--;
  d+=incr2;
     }
     pixelIndex += pixelWidth;
     pixelIndex = pixelWidth*(y) + (x); if((x) >= 0 && (y) >= 0 && (x) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x+1); if((x+1) >= 0 && (y) >= 0 && (x+1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x); if((x) >= 0 && (y+1) >= 0 && (x) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y) + (x-1); if((x-1) >= 0 && (y) >= 0 && (x-1) < pixelWidth && (y) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x); if((x) >= 0 && (y-1) >= 0 && (x) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; if(width == 3){ pixelIndex = pixelWidth*(y+1) + (x+1); if((x+1) >= 0 && (y+1) >= 0 && (x+1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y+1) + (x-1); if((x-1) >= 0 && (y+1) >= 0 && (x-1) < pixelWidth && (y+1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x-1); if((x-1) >= 0 && (y-1) >= 0 && (x-1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; pixelIndex = pixelWidth*(y-1) + (x+1); if((x+1) >= 0 && (y-1) >= 0 && (x+1) < pixelWidth && (y-1) < pixelHeight && z > zb[pixelIndex] && z < frontClip && z > backClip){ zb[pixelIndex] = z; pb[pixelIndex] = rgb;}; };
 }
    }
}
    }
    /** Draw a tmesh object as a collection of points. */
    private void renderSphereObject(Tmesh tmesh){
 int sphereCount = tmesh.np;
 // radius is stored in nx for the sphere
 for(int i = 0; i < sphereCount; i++){
     int c0 = 0;
     if(tmesh.colorStyle == Tmesh.ObjectColor){
  c0 = tmesh.color;
     }else if(tmesh.colorStyle == Tmesh.VertexColor){
  c0 = tmesh.vcolor[i];
     }else{
  c0 = tmesh.vcolor[i];
     }
     drawSphere(tmesh.x[i], tmesh.y[i], tmesh.z[i],
         tmesh.nx[i], c0);
 }
    }
    /** Draw a tmesh object as a collection of points. */
    private void renderDotObject(Tmesh tmesh){
 int dotCount = tmesh.np;
 // radius is stored in nx for the sphere
 for(int i = 0; i < dotCount; i++){
     drawDot(xt[i], yt[i], zt[i], tmesh.vcolor[i]);
 }
    }
    /** Draw a tmesh object as a collection of cylinders. */
    private void renderCylinderObject(Tmesh tmesh){
 int cylinderCount = tmesh.nt;
 // radius is stored in nx for the cylinder
 for(int i = 0; i < cylinderCount; i++){
     int p0 = tmesh.t0[i];
     int p1 = tmesh.t1[i];
     int c0 = 0;
     int c1 = 0;
     if(tmesh.colorStyle == Tmesh.ObjectColor){
  c0 = tmesh.color;
  c1 = tmesh.color;
     }else{
  c0 = tmesh.vcolor[p0];
  c1 = tmesh.vcolor[p1];
     }
     drawCylinder(tmesh.x[p0], tmesh.y[p0], tmesh.z[p0],
    tmesh.x[p1], tmesh.y[p1], tmesh.z[p1],
    c0, c1, tmesh.nx[p0]);
 }
    }
    /** Draw a tmesh object as a collection of points. */
    private void drawPointObject(Tmesh tmesh){
 int np = tmesh.np;
 for(int i = 0; i < np; i++){
     int x = xt[i] >> FixedBits;
     int y = yt[i] >> FixedBits;
     int z = zt[i] >> FixedBits;
     if(x >= 0 && x < pixelWidth &&
        y >= 0 && y < pixelHeight){
  int pos = INDEX(x, y);
  pbuffer[pos] = Color32.white;
     }
 }
    }
    /** Draw object as collection of lines. */
    private void drawLineObject(Tmesh tm){
 int nt = tm.nt;
 double lineWidth = tm.getLineWidth();
 for(int i = 0; i < nt; i++){
     int v0 = tm.t0[i];
     int v1 = tm.t1[i];
     if(lineWidth < 0.0){
  int iw = (int)(-lineWidth + 0.5);
  if(tm.vcolor[v0] == 0 && tm.vcolor[v1] == 0){
      drawLine(tm, v0, v1, tm.color, tm.color, iw);
  }else{
      drawLine(tm, v0, v1, tm.vcolor[v0], tm.vcolor[v1], iw);
  }
     }else{
  if(tm.vcolor[v0] == 0 && tm.vcolor[v1] == 0){
      drawCylinder(tm.x[v0], tm.y[v0], tm.z[v0],
     tm.x[v1], tm.y[v1], tm.z[v1],
     tm.color, tm.color, lineWidth);
  }else{
      drawCylinder(tm.x[v0], tm.y[v0], tm.z[v0],
     tm.x[v1], tm.y[v1], tm.z[v1],
     tm.vcolor[v0], tm.vcolor[v1], lineWidth);
  }
     }
 }
    }
    /** Internal redrawing operations. */
    private void privateRedraw(){
 clearBuffers();
 buildOverallMatrix();
 drawObjects(PreRenderPass);
    }
    /** Public redrawing operations that can be overridden. */
    public void publicRedraw(){
    }
    /** Variable for recording timing info. */
    private long then = 0;
    /** The antialiasing pixel buffer. */
    private int apbuffer[] = null;
    /** The antialiasing z buffer. */
    private int azbuffer[] = null;
    /** The original pixel buffer. */
    public int opbuffer[] = null;
    /** The original z buffer. */
    private int ozbuffer[] = null;
    private int realw = 0;
    private int realh = 0;
    private int realPixelCount = 0;
    /** Initialise antialiasing. */
    private void setupAntiAlias(){
 if(antialiasModeChanged){
     if(antialias){
  realw = pixelWidth;
  realh = pixelHeight;
  pixelWidth = realw * 2;
  pixelHeight = realh * 2;
  pixelCount = pixelWidth * pixelHeight;
  if(apbuffer == null || apbuffer.length < pixelCount){
      apbuffer = new int[pixelCount];
      azbuffer = new int[pixelCount];
  }
  opbuffer = pbuffer;
  ozbuffer = zbuffer;
  pbuffer = apbuffer;
  zbuffer = azbuffer;
     }else{
  if(opbuffer != null){
      pbuffer = opbuffer;
      zbuffer = ozbuffer;
      pixelWidth = realw;
      pixelHeight = realh;
      pixelCount = pixelWidth * pixelHeight;
      opbuffer = null;
      ozbuffer = null;
  }
     }
     antialiasModeChanged = false;
 }
    }
    /** Redraw the image. */
    public void redraw(){
 triangleRaysIntersected = 0;
 triangleRaysCast = 0;
 if(shadowMode == ShadowsAccumulate){
     ShadowCache.clearShadowCaches();
 }else if(shadowMode == ShadowsOn){
     ShadowCache.setupShadowCaches((Light)lights.get(0),
       getOverallScale());
 }
 // set the range of z-coordinates we have seen
 zmin = Integer.MAX_VALUE;
 zmax = Integer.MIN_VALUE;
 setupAntiAlias();
 privateRedraw();
 //drawObjects();
 publicRedraw();
 //postProcess();
 frameCount++;
    }
    /** Draw the logo if there is one displayed. */
    private void drawLogo(){
 if(logo != null){
     drawDirectString(4, 12, Color32.white, logo);
 }
    }
    /** Should we display the status string. */
    private boolean displayStatusString = true;
    /** Set whether or not to display the status string. */
    public void setDisplayStatusString(boolean b){
        displayStatusString = b;
    }
    /** Draw the status string if there is one displayed. */
    private void drawStatusString(){
        if(displayStatusString){
            if(statusString != null){
                //g.setColor(Color.white);
                drawDirectString(3, pixelHeight - 3, Color32.white, statusString);
                //g.drawString(statusString, 3, height - 3);
            }
        }
    }
    /** Set the current logo. */
    public void setLogo(String newLogo){
 logo = newLogo;
    }
    /** Get the current logo. */
    public String getLogo(){
 return logo;
    }
    /** Set the current StatusString. */
    public void setStatusString(String newStatusString){
 statusString = newStatusString;
    }
    /** Get the current StatusString. */
    public String getStatusString(){
 return statusString;
    }
    /**
     * Resize the renderer.
     * Only reallocate the buffers if the window
     * gets bigger.
     */
    public void setSize(int width, int height){
 pixelWidth = width;
 pixelHeight = height;
 pixelCount = pixelWidth * pixelHeight;
 if(pbuffer == null ||
    pbuffer.length < pixelCount ||
    antialias){
     pbuffer = new int[pixelCount];
     zbuffer = new int[pixelCount];
     //apbuffer = null;
     //azbuffer = null;
     antialiasModeChanged = true;
 }
    }
    /** The numbe of samples along each axis. */
    private int samples = 1;
    /** Set the number of samples. */
    public void setSamples(int s){
 samples = s;
    }
    /** Get the number of samples. */
    public int getSamples(){
 return samples;
    }
    /** Entries for the color map cache. */
    private static int colorMapCacheSize = 16;
    private transient int colorMapCacheCount = 0;
    private int colorMapCacheMisses = 0;
    private transient int colorMapCache[][] = new int[colorMapCacheSize][];
    private transient int colorMapCacheColor[] = new int[colorMapCacheSize];
    private int color = 0;
    private void initialiseColorMaps(){
        // pandering to the xml serialisation limitiations
        if(colorMap == null) colorMap = new int[MapEntries];
        if(intensityMap == null) intensityMap = new int[MapEntries];
        if(diffuseMap == null) diffuseMap = new int[MapEntries];
        if(highlightMap == null) highlightMap = new int[MapEntries];
        if(shadowMap == null) shadowMap = new int[MapEntries];
        if(colorMapCache == null) colorMapCache = new int[colorMapCacheSize][];
        if(colorMapCacheColor == null) colorMapCacheColor = new int[colorMapCacheSize];
    }
    /** Set the material color. */
    public void setColor(int newc){
        if(colorMap == null){
            initialiseColorMaps();
        }
 if(newc == color) return;
 if(!lightMapCalculated){
     calculateLightMap();
 }
 color = newc;
 colorInitialised = false;
 for(int i = 0; i < colorMapCacheCount; i++){
     if(colorMapCacheColor[i] == newc){
  colorMap = colorMapCache[i];
  colorInitialised = true;
  return;
     }
 }
 int slot = -1;
 if(colorMapCacheCount < colorMapCacheSize){
     slot = colorMapCacheCount++;
 }else{
     slot = 0;
     colorMapCacheMisses++;
     if((colorMapCacheMisses % 100) == 0){
  System.out.println("color map cache misses " +
       colorMapCacheMisses);
     }
 }
 colorMap = new int[MapEntries];
 colorMapCache[slot] = colorMap;
 colorMapCacheColor[slot] = newc;
 initialiseColor();
    }
    /** Calculate vertex color using lookup table. */
    private void lightVertex(Vertex v){
 int lutID = (((v.nx)>>FixedBits) + (((v.ny)>>FixedBits)<<NormalBits));
 int color = colorMap[lutID];
 v.r = ((color>>16)&255)<<FixedBits;
 v.g = ((color>>8)&255)<<FixedBits;
 v.b = ((color)&255)<<FixedBits;
    }
    /** Store vertex colors. */
    private void vertexColor(Vertex v, int color){
 v.r = ((color>>16)&255)<<FixedBits;
 v.g = ((color>>8)&255)<<FixedBits;
 v.b = ((color)&255)<<FixedBits;
    }
    /** Fill the color lookup table. */
    private void initialiseColor(){
 int rcomp = (color>>16) & 255;
 int gcomp = (color>>8) & 255;
 int bcomp = color & 255;
 int rint, gint, bint;
 int pixel, overflow, c, s;
 int dcolor;
 int dmap[] = diffuseMap, smap[] = highlightMap;
 for(int i = 0; i < MapEntries; i++){
     // calculate diffuse component
     dcolor = dmap[i];
     rint = (dcolor >>16)&255;
     gint = (dcolor >>8)&255;
     bint = dcolor&255;
     c = (((rcomp * rint) >> 8)<<16) |
  ((gcomp*gint)&0xff00) | ((bcomp*bint)>>8);
     // add in specular component
     s = smap[i];
     pixel = Color32.add(c, s);
     colorMap[i] = pixel;
 }
 colorInitialised = true;
    }
    private static double sqrtTable[] = null;
    private static final double fastSqrt(double d){
 try {
     if(sqrtTable == null){
  sqrtTable = new double[10000];
  for(int i = 0; i < 10000; i++){
      sqrtTable[i] = Math.sqrt(i);
  }
     }
     return sqrtTable[(int)d];
 }catch(Exception e){
     return Math.sqrt(d);
 }
    }
    private boolean firstTime = true;
    /** Calculate the light map for the current lights. */
    protected void calculateLightMap(){
 if(false){
     if(firstTime){
  System.out.println("initialising light tables... " +
       MapEntries + " Entries");
     }
 }
 // clear out any sphere bit map caches.
 for(int i = 0; i < MaxCache; i++){
     szCache[i] = null;
     scCache[i] = null;
 }
 for(int i = 0; i < colorMapCacheSize; i++){
     colorMapCache[i] = null;
 }
 cacheCount = 0;
 colorMapCacheCount = 0;
 color = 0;
 //calculateSphereMap();
 double dnx, dny, dnz, angle;
 int tableEntry = 0;
 int dmap[] = diffuseMap;
 int smap[] = highlightMap;
 int imap[] = intensityMap;
 int shadowrgb[] = new int[3];
 int drgb[] = new int[3];
 int srgb[] = new int[3];
 for(int ny = -NormalSamples; ny < NormalSamples; ny++) {
     dny = (double)ny / NormalSamples;
     for(int nx = -NormalSamples; nx < NormalSamples; nx++) {
  // this must match the definition of LUT(x,y)
  // but without the >>FixedBits parts
  tableEntry = (nx+NormalSamples) + ((ny+NormalSamples)*(2*NormalSamples));
  dnx = (double)nx/NormalSamples;
  dnz = 1.0 - Math.sqrt(dnx * dnx + dny * dny);
  if(lightingModel == CartoonLightingModel){
      drgb[0] = drgb[1] = drgb[2] = 255;
      srgb[0] = srgb[1] = srgb[2] = 0;
      shadowrgb[0] = shadowrgb[1] = shadowrgb[2] = 128;
      if(dnz < cartoonNormalCutoff){
   drgb[0] = drgb[1] = drgb[2] = 0;
   shadowrgb[0] = shadowrgb[1] = shadowrgb[2] = 0;
      }
  }else{
      generateColour(dnx, dny, dnz, drgb, srgb, shadowrgb);
  }
  imap[tableEntry] = (drgb[0]+drgb[1]+drgb[2])/3;
  dmap[tableEntry] = Color32.getClampColor(drgb[0],drgb[1],drgb[2]);
  smap[tableEntry] = Color32.getClampColor(srgb[0],srgb[1],srgb[2]);
  shadowMap[tableEntry] = Color32.getClampColor(shadowrgb[0],
            shadowrgb[1],
            shadowrgb[2]);
     }
 }
 lightMapCalculated = true;
 if(false){
     if(firstTime){
  System.out.println("done");
  firstTime = false;
     }
 }
    }
    public void setPowFactor(double d){
        powFactor = d;
        lightMapCalculated = false;
        cacheCount = 0;
    }
    public double powFactor = 1.0;
    public void setWrapAngle(double d){
        wrapAngle = d;
        cosWrapAngle = Math.cos(wrapAngle);
        lightMapCalculated = false;
        cacheCount = 0;
    }
    public double wrapAngle = -1.0;
    public double cosWrapAngle = Math.cos(wrapAngle);
    private static final double PiBy2 = 0.5 * Math.PI;
    /** Calculate the color for this normal. */
    private synchronized void generateColour(double nx, double ny, double nz,
                                             int drgb[], int srgb[],
                                             int shadowrgb[]){
 srgb[0] = 0;
 srgb[1] = 0;
 srgb[2] = 0;
 shadowrgb[0] = ambientr;
 shadowrgb[1] = ambientg;
 shadowrgb[2] = ambientb;
 drgb[0] = ambientr;
 drgb[1] = ambientg;
 drgb[2] = ambientb;
        if(wrapAngle < 0.0){
     double w = Settings.getDouble("config", "wrapangle");
            // convert to radians
            w *= Math.PI / 180.0;
            setWrapAngle(w);
        }
 for (int i = 0; i < lights.size(); i++) {
     Light l = (Light)lights.get(i);
     if(l.on){
  int diffuse = l.diffuse;
  int specular = l.specular;
  if(nz > 1.0) nz = 1.0;
  // Actually evaluate the lighting model directly
  double len = Math.sqrt(nx*nx+ny*ny+nz*nz);
  nx/=len;
  ny/=len;
  nz/=len;
  double cos = cos(l.pos[0], l.pos[1], l.pos[2], nx, ny, nz);
  if(powFactor < 1.0){
      cos = Math.pow(cos, powFactor);
  }
  // lambert approximation
  double dcos = cos;
  if(wrapAngle > PiBy2){
      dcos = 1.0 - Math.acos(cos)/wrapAngle;
      dcos = (dcos > 0) ? dcos : 0;
  }else{
      dcos = (cos > 0) ? cos : 0;
  }
  double phong = 0.0;
  // if phong power is really low turn it off
  if(Math.abs(l.power) < 0.1){
      phong = 0.0;
  }else{
      phong = Math.abs(Math.pow(cos, l.power));
  }
  for(int comp = 0; comp < 3; comp++){
      drgb[comp] += (int)(Color32.getComponent(diffuse, comp) * dcos);
      if(i != 0){
   shadowrgb[comp] += (int)(Color32.getComponent(diffuse, comp) * dcos);
      }
      srgb[comp] += (int)(Color32.getComponent(specular, comp) * phong);
  }
     }
 }
        // clamp intensities
 for(int comp = 0; comp < 3; comp++){
     if(drgb[comp] > 255) drgb[comp] = 255;
     if(srgb[comp] > 255) srgb[comp] = 255;
     if(shadowrgb[comp] > 255) shadowrgb[comp] = 255;
 }
    }
    /** Calculate the cos of the angle between two vectors. */
    private static double cos(double xa, double ya, double za,
        double xb, double yb, double zb){
 double la = Math.sqrt(xa*xa + ya*ya + za*za);
 double lb = Math.sqrt(xb*xb + yb*yb + zb*zb);
 return (xa*xb + ya*yb + za*zb)/(la*lb);
    }
    /** Print out a fixed format coordinate. */
    private void print(String label, int a[]){
 System.out.println(label + " " + a[0] + " " + a[1] + " " + a[2]);
    }
    /** Number of bits+1 in NormalSamples. */
    private static final int NormalBits = 8;
    /** The number of samples for lookup tables. */
    private static final int NormalSamples = (1 << (NormalBits-1));
    /** The number of entries in a map array. */
    private static final int MapEntries = NormalSamples * NormalSamples * 4;
    /** The number of samples * 2 - 1. */
    private static final int NormalSamples2 = 2 * NormalSamples - 1;
    /** The intensity color map entries. */
    private transient int intensityMap[] = new int[MapEntries];
    /** The diffuse color map entries. */
    private transient int diffuseMap[] = new int[MapEntries];
    /** The shadow color map entries. */
    private transient int shadowMap[] = new int[MapEntries];
    /** The specular color map entries. */
    private transient int highlightMap[] = new int[MapEntries];
    /** The current color map. */
    private transient int colorMap[] = null;
    /* Clipping attributes. */
    private static int XMinClip = 1;
    private static int XMaxClip = 2;
    private static int YMinClip = 4;
    private static int YMaxClip = 8;
    private static int ZMinClip = 16;
    private static int ZMaxClip = 32;
    private static int UMinClip = 64;
    private static int UMaxClip = 128;
    private static int VMinClip = 256;
    private static int VMaxClip = 512;
    private static int NormalClip = 1024;
    		
    // Call back when the surface is first created or re-created.
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {  		
    	//
    }
    
    // Call back when the surface is first created or re-created.
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {  		
    	;
    }
    
    // Call back to draw the current frame.
    @Override
    public void onDrawFrame(GL10 gl) {

    }
}
/**
 * Store the info for a vertex.
 * These are operated on by the rasterizer.
 */
class Vertex {
    int x, y, z, nx, ny, u, v, r, g, b;
}
